evento
19/11, 23 - 21/11, 23
Participe de sessões on-line no Microsoft Ignite criadas para expandir suas habilidades e ajudá-lo a lidar com os problemas complexos de hoje.
Registe-se agoraEste browser já não é suportado.
Atualize para o Microsoft Edge para tirar partido das mais recentes funcionalidades, atualizações de segurança e de suporte técnico.
Nota
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 9 deste artigo.
Aviso
Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, consulte a Política de Suporte do .NET e do .NET Core. Para a versão atual, consulte a versão .NET 9 deste artigo.
Importante
Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.
Para a versão atual, consulte a versão .NET 9 deste artigo.
Este artigo explica os recursos de associação de dados para componentes Razor e elementos DOM em aplicativos Blazor.
Os componentes Razor fornecem recursos de associação de dados com o atributo de diretiva @bind
Razor e um valor de campo, propriedade ou expressão Razor.
O exemplo abaixo associa:
<input>
para o campo C# inputValue
.<input>
para a propriedade C# InputValue
.Quando um elemento <input>
perde o foco, seu campo ou propriedade associada é atualizado.
Bind.razor
:
@page "/bind"
<PageTitle>Bind</PageTitle>
<h1>Bind Example</h1>
<p>
<label>
inputValue:
<input @bind="inputValue" />
</label>
</p>
<p>
<label>
InputValue:
<input @bind="InputValue" />
</label>
</p>
<ul>
<li><code>inputValue</code>: @inputValue</li>
<li><code>InputValue</code>: @InputValue</li>
</ul>
@code {
private string? inputValue;
private string? InputValue { get; set; }
}
@page "/bind"
<PageTitle>Bind</PageTitle>
<h1>Bind Example</h1>
<p>
<label>
inputValue:
<input @bind="inputValue" />
</label>
</p>
<p>
<label>
InputValue:
<input @bind="InputValue" />
</label>
</p>
<ul>
<li><code>inputValue</code>: @inputValue</li>
<li><code>InputValue</code>: @InputValue</li>
</ul>
@code {
private string? inputValue;
private string? InputValue { get; set; }
}
@page "/bind"
<p>
<input @bind="inputValue" />
</p>
<p>
<input @bind="InputValue" />
</p>
<ul>
<li><code>inputValue</code>: @inputValue</li>
<li><code>InputValue</code>: @InputValue</li>
</ul>
@code {
private string? inputValue;
private string? InputValue { get; set; }
}
@page "/bind"
<p>
<input @bind="inputValue" />
</p>
<p>
<input @bind="InputValue" />
</p>
<ul>
<li><code>inputValue</code>: @inputValue</li>
<li><code>InputValue</code>: @InputValue</li>
</ul>
@code {
private string? inputValue;
private string? InputValue { get; set; }
}
@page "/bind"
<p>
<input @bind="inputValue" />
</p>
<p>
<input @bind="InputValue" />
</p>
<ul>
<li><code>inputValue</code>: @inputValue</li>
<li><code>InputValue</code>: @InputValue</li>
</ul>
@code {
private string inputValue;
private string InputValue { get; set; }
}
@page "/bind"
<p>
<input @bind="inputValue" />
</p>
<p>
<input @bind="InputValue" />
</p>
<ul>
<li><code>inputValue</code>: @inputValue</li>
<li><code>InputValue</code>: @InputValue</li>
</ul>
@code {
private string inputValue;
private string InputValue { get; set; }
}
A caixa de texto é atualizada na interface do usuário somente quando o componente é renderizado, não em resposta à alteração do valor do campo ou da propriedade. Como os componentes são renderizados após a execução do código do manipulador de eventos, as atualizações de propriedade são normalmente refletidas na IU imediatamente após o disparo de um manipulador de eventos.
Como uma demonstração de como a associação de dados é composta em HTML, o exemplo a seguir associa a propriedade InputValue
aos atributos value
e onchange
do segundo elemento <input>
(change
). O segundo elemento <input>
no exemplo a seguir é uma demonstração de conceito e não se destina a sugerir como você deve associar dados em componentes Razor.
BindTheory.razor
:
@page "/bind-theory"
<PageTitle>Bind Theory</PageTitle>
<h1>Bind Theory Example</h1>
<p>
<label>
Normal Blazor binding:
<input @bind="InputValue" />
</label>
</p>
<p>
<label>
Demonstration of equivalent HTML binding:
<input value="@InputValue" @onchange="@((ChangeEventArgs __e) =>
InputValue = __e?.Value?.ToString())" />
</label>
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string? InputValue { get; set; }
}
@page "/bind-theory"
<PageTitle>Bind Theory</PageTitle>
<h1>Bind Theory Example</h1>
<p>
<label>
Normal Blazor binding:
<input @bind="InputValue" />
</label>
</p>
<p>
<label>
Demonstration of equivalent HTML binding:
<input value="@InputValue" @onchange="@((ChangeEventArgs __e) =>
InputValue = __e?.Value?.ToString())" />
</label>
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string? InputValue { get; set; }
}
@page "/bind-theory"
<p>
<label>
Normal Blazor binding:
<input @bind="InputValue" />
</label>
</p>
<p>
<label>
Demonstration of equivalent HTML binding:
<input value="@InputValue"
@onchange="@((ChangeEventArgs __e) => InputValue = __e?.Value?.ToString())" />
</label>
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string? InputValue { get; set; }
}
@page "/bind-theory"
<p>
<label>
Normal Blazor binding:
<input @bind="InputValue" />
</label>
</p>
<p>
<label>
Demonstration of equivalent HTML binding:
<input value="@InputValue"
@onchange="@((ChangeEventArgs __e) => InputValue = __e?.Value?.ToString())" />
</label>
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string? InputValue { get; set; }
}
@page "/bind-theory"
<p>
<label>
Normal Blazor binding:
<input @bind="InputValue" />
</label>
</p>
<p>
<label>
Demonstration of equivalent HTML binding:
<input value="@InputValue"
@onchange="@((ChangeEventArgs __e) => InputValue = __e.Value.ToString())" />
</label>
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string InputValue { get; set; }
}
@page "/bind-theory"
<p>
<label>
Normal Blazor binding:
<input @bind="InputValue" />
</label>
</p>
<p>
<label>
Demonstration of equivalent HTML binding:
<input value="@InputValue"
@onchange="@((ChangeEventArgs __e) => InputValue = __e.Value.ToString())" />
</label>
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string InputValue { get; set; }
}
Quando o componente BindTheory
é renderizado, o value
do elemento <input>
de demonstração de HTML vem da propriedade InputValue
. Quando o usuário insere um valor na caixa de texto e altera o foco do elemento, o evento onchange
é disparado e a propriedade InputValue
é definida como o valor alterado. Na realidade, a execução de código é mais complexa porque @bind
trata casos em que as conversões de tipo são executadas. Em geral, @bind
associa o valor atual de uma expressão ao atributo value
de <input>
e trata as alterações usando o manipulador registrado.
Associe uma propriedade ou campo em outros eventos DOM com a inclusão de um atributo @bind:event="{EVENT}"
com um evento DOM para o espaço reservado {EVENT}
. O exemplo a seguir associa a propriedade InputValue
ao valor do elemento <input>
quando o evento oninput
do elemento (input
) é disparado. Ao contrário do evento onchange
(change
), que é acionado quando o elemento perde o foco, oninput
(input
) é acionado quando o valor da caixa de texto é alterado.
Page/BindEvent.razor
:
@page "/bind-event"
<PageTitle>Bind Event</PageTitle>
<h1>Bind Event Example</h1>
<p>
<label>
InputValue:
<input @bind="InputValue" @bind:event="oninput" />
</label>
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string? InputValue { get; set; }
}
@page "/bind-event"
<PageTitle>Bind Event</PageTitle>
<h1>Bind Event Example</h1>
<p>
<label>
InputValue:
<input @bind="InputValue" @bind:event="oninput" />
</label>
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string? InputValue { get; set; }
}
@page "/bind-event"
<p>
<input @bind="InputValue" @bind:event="oninput" />
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string? InputValue { get; set; }
}
@page "/bind-event"
<p>
<input @bind="InputValue" @bind:event="oninput" />
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string? InputValue { get; set; }
}
@page "/bind-event"
<p>
<input @bind="InputValue" @bind:event="oninput" />
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string InputValue { get; set; }
}
@page "/bind-event"
<p>
<input @bind="InputValue" @bind:event="oninput" />
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string InputValue { get; set; }
}
A associação de atributo Razor diferencia maiúsculas de minúsculas:
@bind
e @bind:event
são válidos.@Bind
/@Bind:Event
(letras maiúsculas B
e E
) ou @BIND
/@BIND:EVENT
(todas as letras maiúsculas) são inválidos.Para executar a lógica assíncrona após a associação, use @bind:after="{DELEGATE}"
, em que o espaço reservado {DELEGATE}
é um delegado C# (método). Um delegado C# atribuído não é executado até que o valor associado seja atribuído de forma síncrona.
Não há suporte ao uso de um parâmetro de retorno de chamada de evento (EventCallback
/EventCallback<T>
) com @bind:after
. Transmita um método que retorne um Action ou Task a @bind:after
.
No exemplo a seguir:
value
do elemento <input>
é vinculado ao campo searchText
de forma síncrona.PerformSearch
é executado de forma assíncrona: onchange
) após o valor ser alterado.oninput
) ser pressionada na segunda caixa.PerformSearch
chama um serviço com um método assíncrono (FetchAsync
) para retornar os resultados da pesquisa.@inject ISearchService SearchService
<input @bind="searchText" @bind:after="PerformSearch" />
<input @bind="searchText" @bind:event="oninput" @bind:after="PerformSearch" />
@code {
private string? searchText;
private string[]? searchResult;
private async Task PerformSearch() =>
searchResult = await SearchService.FetchAsync(searchText);
}
Exemplos adicionais
BindAfter.razor
:
@page "/bind-after"
@using Microsoft.AspNetCore.Components.Forms
<h1>Bind After Examples</h1>
<h2>Elements</h2>
<input type="text" @bind="text" @bind:after="() => { }" />
<input type="text" @bind="text" @bind:after="After" />
<input type="text" @bind="text" @bind:after="AfterAsync" />
<h2>Components</h2>
<InputText @bind-Value="text" @bind-Value:after="() => { }" />
<InputText @bind-Value="text" @bind-Value:after="After" />
<InputText @bind-Value="text" @bind-Value:after="AfterAsync" />
@code {
private string text = "";
private void After() {}
private Task AfterAsync() { return Task.CompletedTask; }
}
Para obter mais informações sobre o componente InputText
, confira Componentes de entrada do ASP.NET Core Blazor.
Os componentes dão suporte à associação de dados bidirecional definindo um par de parâmetros:
@bind:get
: especifica o valor a ser associado.@bind:set
: especifica um retorno de chamada para quando o valor é alterado.Os modificadores @bind:get
e @bind:set
são sempre usados juntos.
Exemplos
BindGetSet.razor
:
@page "/bind-get-set"
@using Microsoft.AspNetCore.Components.Forms
<h1>Bind Get Set Examples</h1>
<h2>Elements</h2>
<input type="text" @bind:get="text" @bind:set="(value) => { text = value; }" />
<input type="text" @bind:get="text" @bind:set="Set" />
<input type="text" @bind:get="text" @bind:set="SetAsync" />
<h2>Components</h2>
<InputText @bind-Value:get="text" @bind-Value:set="(value) => { text = value; }" />
<InputText @bind-Value:get="text" @bind-Value:set="Set" />
<InputText @bind-Value:get="text" @bind-Value:set="SetAsync" />
@code {
private string text = "";
private void Set(string value)
{
text = value;
}
private Task SetAsync(string value)
{
text = value;
return Task.CompletedTask;
}
}
Para obter mais informações sobre o componente InputText
, confira Componentes de entrada do ASP.NET Core Blazor.
Para obter outro exemplo de uso de @bind:get
e @bind:set
consulte a seção Associar em mais de dois componentes posteriormente neste artigo.
A associação de atributo Razor diferencia maiúsculas de minúsculas:
@bind
, @bind:event
e @bind:after
são válidos.@Bind
/@bind:Event
/@bind:aftEr
(letras maiúsculas ) ou @BIND
/@BIND:EVENT
/@BIND:AFTER
(todas as letras maiúsculas) são inválidos.@bind:get
/@bind:set
e evite manipuladores de eventos para associação de dados bidirecionalNão é possível implementar a associação de dados bidirecional com um manipulador de eventos. Use modificadores @bind:get
/@bind:set
para associação de dados bidirecional.
Considere a seguinte abordagem disfuncional para associação de dados bidirecional usando um manipulador de eventos:
<p>
<input value="@inputValue" @oninput="OnInput" />
</p>
<p>
<code>inputValue</code>: @inputValue
</p>
@code {
private string? inputValue;
private void OnInput(ChangeEventArgs args)
{
var newValue = args.Value?.ToString() ?? string.Empty;
inputValue = newValue.Length > 4 ? "Long!" : newValue;
}
}
O manipulador de eventos OnInput
atualiza o valor de inputValue
para Long!
depois que um quarto caractere é fornecido. No entanto, o usuário pode continuar adicionando caracteres ao valor do elemento na interface do usuário. O valor de inputValue
não é associado de volta ao valor do elemento a cada pressionamento de tecla. O exemplo anterior só é capaz de associar dados unidirecionalmente.
O motivo desse comportamento é que o Blazor não está ciente de que seu código pretende modificar o valor de inputValue
no manipulador de eventos. O Blazor não tenta forçar valores de elemento DOM e valores de variável .NET a corresponderem, a menos que estejam associados à sintaxe @bind
. Nas versões anteriores do Blazor, a associação de dados bidirecional era implementada com a associação do elemento a uma propriedade e o controle do valor da propriedade com seu setter. No ASP.NET Core no .NET 7 ou posterior, a sintaxe do modificador @bind:get
/@bind:set
é usada para implementar a associação de dados bidirecional, como demonstra o próximo exemplo.
@bind:get
/@bind:set
na associação de dados bidirecional:
<p>
<input @bind:event="oninput" @bind:get="inputValue" @bind:set="OnInput" />
</p>
<p>
<code>inputValue</code>: @inputValue
</p>
@code {
private string? inputValue;
private void OnInput(string value)
{
var newValue = value ?? string.Empty;
inputValue = newValue.Length > 4 ? "Long!" : newValue;
}
}
O uso de modificadores @bind:get
/@bind:set
controla o valor subjacente de inputValue
por meio de @bind:set
e associa o valor de inputValue
ao valor do elemento por meio de @bind:get
. O exemplo anterior demonstra a abordagem correta para implementar a associação de dados bidirecional.
Acessadores C#get
e set
podem ser usados para criar um comportamento de formato de associação personalizado, como demonstra o componente DecimalBinding
a seguir. O componente associa uma decimal positiva ou negativa com até três casas decimais a um elemento <input>
por meio de uma propriedade string
(DecimalValue
).
DecimalBinding.razor
:
@page "/decimal-binding"
@using System.Globalization
<PageTitle>Decimal Binding</PageTitle>
<h1>Decimal Binding Example</h1>
<p>
<label>
Decimal value (±0.000 format):
<input @bind="DecimalValue" />
</label>
</p>
<p>
<code>decimalValue</code>: @decimalValue
</p>
@code {
private decimal decimalValue = 1.1M;
private NumberStyles style =
NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign;
private CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");
private string DecimalValue
{
get => decimalValue.ToString("0.000", culture);
set
{
if (Decimal.TryParse(value, style, culture, out var number))
{
decimalValue = Math.Round(number, 3);
}
}
}
}
@page "/decimal-binding"
@using System.Globalization
<PageTitle>Decimal Binding</PageTitle>
<h1>Decimal Binding Example</h1>
<p>
<label>
Decimal value (±0.000 format):
<input @bind="DecimalValue" />
</label>
</p>
<p>
<code>decimalValue</code>: @decimalValue
</p>
@code {
private decimal decimalValue = 1.1M;
private NumberStyles style =
NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign;
private CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");
private string DecimalValue
{
get => decimalValue.ToString("0.000", culture);
set
{
if (Decimal.TryParse(value, style, culture, out var number))
{
decimalValue = Math.Round(number, 3);
}
}
}
}
@page "/decimal-binding"
@using System.Globalization
<p>
<label>
Decimal value (±0.000 format):
<input @bind="DecimalValue" />
</label>
</p>
<p>
<code>decimalValue</code>: @decimalValue
</p>
@code {
private decimal decimalValue = 1.1M;
private NumberStyles style =
NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign;
private CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");
private string DecimalValue
{
get => decimalValue.ToString("0.000", culture);
set
{
if (Decimal.TryParse(value, style, culture, out var number))
{
decimalValue = Math.Round(number, 3);
}
}
}
}
@page "/decimal-binding"
@using System.Globalization
<p>
<label>
Decimal value (±0.000 format):
<input @bind="DecimalValue" />
</label>
</p>
<p>
<code>decimalValue</code>: @decimalValue
</p>
@code {
private decimal decimalValue = 1.1M;
private NumberStyles style =
NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign;
private CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");
private string DecimalValue
{
get => decimalValue.ToString("0.000", culture);
set
{
if (Decimal.TryParse(value, style, culture, out var number))
{
decimalValue = Math.Round(number, 3);
}
}
}
}
@page "/decimal-binding"
@using System.Globalization
<p>
<label>
Decimal value (±0.000 format):
<input @bind="DecimalValue" />
</label>
</p>
<p>
<code>decimalValue</code>: @decimalValue
</p>
@code {
private decimal decimalValue = 1.1M;
private NumberStyles style =
NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign;
private CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");
private string DecimalValue
{
get => decimalValue.ToString("0.000", culture);
set
{
if (Decimal.TryParse(value, style, culture, out var number))
{
decimalValue = Math.Round(number, 3);
}
}
}
}
@page "/decimal-binding"
@using System.Globalization
<p>
<label>
Decimal value (±0.000 format):
<input @bind="DecimalValue" />
</label>
</p>
<p>
<code>decimalValue</code>: @decimalValue
</p>
@code {
private decimal decimalValue = 1.1M;
private NumberStyles style =
NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign;
private CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");
private string DecimalValue
{
get => decimalValue.ToString("0.000", culture);
set
{
if (Decimal.TryParse(value, style, culture, out var number))
{
decimalValue = Math.Round(number, 3);
}
}
}
}
Nota
A associação bidirecional a uma propriedade com acessadores get
/set
requer o descarte da Task retornada por EventCallback.InvokeAsync. Para associação de dados bidirecional, é recomendável usar modificadores @bind:get
/@bind:set
. Para obter mais informações, consulte as diretrizes @bind:get
/@bind:set
descritas anteriormente neste artigo.
Nota
A associação bidirecional a uma propriedade com acessadores get
/set
requer o descarte da Task retornada por EventCallback.InvokeAsync. Para associação de dados bidirecional no ASP.NET Core no .NET 7 ou posterior, é recomendável usar modificadores @bind:get
/@bind:set
, que são descritos em versões 7.0 ou posteriores deste artigo.
A associação dá suporte à seleção de opção multiple
com elementos <select>
. O evento @onchange
fornece uma matriz dos elementos selecionados por meio de argumentos de evento (ChangeEventArgs
). O valor precisa ser associado a um tipo de matriz.
BindMultipleInput.razor
:
@page "/bind-multiple-input"
<h1>Bind Multiple <code>input</code>Example</h1>
<p>
<label>
Select one or more cars:
<select @onchange="SelectedCarsChanged" multiple>
<option value="audi">Audi</option>
<option value="jeep">Jeep</option>
<option value="opel">Opel</option>
<option value="saab">Saab</option>
<option value="volvo">Volvo</option>
</select>
</label>
</p>
<p>
Selected Cars: @string.Join(", ", SelectedCars)
</p>
<p>
<label>
Select one or more cities:
<select @bind="SelectedCities" multiple>
<option value="bal">Baltimore</option>
<option value="la">Los Angeles</option>
<option value="pdx">Portland</option>
<option value="sf">San Francisco</option>
<option value="sea">Seattle</option>
</select>
</label>
</p>
<span>
Selected Cities: @string.Join(", ", SelectedCities)
</span>
@code {
public string[] SelectedCars { get; set; } = new string[] { };
public string[] SelectedCities { get; set; } = new[] { "bal", "sea" };
private void SelectedCarsChanged(ChangeEventArgs e)
{
if (e.Value is not null)
{
SelectedCars = (string[])e.Value;
}
}
}
Para obter informações sobre como cadeias de caracteres e valores null
vazios são tratados na associação de dados, confira a seção Associação de opções de elemento <select>
aos valores null
do objeto C#.
Não há nenhuma maneira sensata de representar um valor de opção de elemento <select>
como um valor de objeto C# null
porque:
null
. O equivalente mais próximo a null
em HTML é a ausência do atributo HTML value
do elemento <option>
.<option>
sem atributo value
, o navegador tratará o valor como o conteúdo de texto da <option>
desse elemento.A estrutura Blazor não tenta suprimir o comportamento padrão porque isso envolveria:
O equivalente null
mais plausível em HTML é uma cadeia de caracteres vazia value
. A estrutura Blazor trata null
em conversões de cadeia de caracteres vazias como associação bidirecional a um valor de <select>
.
Quando um usuário fornece um valor indissociável para um elemento associado a dados, o valor não útil é revertido automaticamente para seu valor anterior quando o evento de associação é disparado.
Considere o componente a seguir, em que um elemento <input>
está associado a um tipo int
com um valor inicial de 123
.
UnparsableValues.razor
:
@page "/unparsable-values"
<PageTitle>Unparsable Values</PageTitle>
<h1>Unparsable Values Example</h1>
<p>
<label>
inputValue:
<input @bind="inputValue" />
</label>
</p>
<p>
<code>inputValue</code>: @inputValue
</p>
@code {
private int inputValue = 123;
}
@page "/unparsable-values"
<PageTitle>Unparsable Values</PageTitle>
<h1>Unparsable Values Example</h1>
<p>
<label>
inputValue:
<input @bind="inputValue" />
</label>
</p>
<p>
<code>inputValue</code>: @inputValue
</p>
@code {
private int inputValue = 123;
}
@page "/unparseable-values"
<p>
<input @bind="inputValue" />
</p>
<p>
<code>inputValue</code>: @inputValue
</p>
@code {
private int inputValue = 123;
}
@page "/unparseable-values"
<p>
<input @bind="inputValue" />
</p>
<p>
<code>inputValue</code>: @inputValue
</p>
@code {
private int inputValue = 123;
}
@page "/unparseable-values"
<p>
<input @bind="inputValue" />
</p>
<p>
<code>inputValue</code>: @inputValue
</p>
@code {
private int inputValue = 123;
}
@page "/unparseable-values"
<p>
<input @bind="inputValue" />
</p>
<p>
<code>inputValue</code>: @inputValue
</p>
@code {
private int inputValue = 123;
}
A associação se aplica ao evento onchange
do elemento. Se o usuário atualizar o valor da entrada da caixa de texto para 123.45
e alterar o foco, o valor do elemento será revertido para 123
quando onchange
for acionado. Quando o valor 123.45
é rejeitado em favor do valor original de 123
, o usuário entende que seu valor não foi aceito.
Para o evento oninput
(@bind:event="oninput"
), uma reversão de valor ocorre após algum pressionamento de tecla que introduza um valor não analisável. Ao ter como alvo o evento oninput
com um tipo de associação de int
, um usuário é impedido de digitar um caractere de ponto (.
). Um caractere dot (.
) é imediatamente removido, de modo que o usuário recebe comentários imediatos de que apenas números inteiros são permitidos. Há cenários em que a reversão do valor do evento oninput
não é ideal, por exemplo, quando o usuário deve ter permissão para limpar um valor <input>
não analisável. As alternativas incluem:
oninput
. Use o evento padrão onchange
, em que um valor inválido não é revertido até que o elemento perca o foco.int?
ou string
e usar modificadores /@bind:set
@bind:get
(descritos anteriormente neste artigo) ou associar a uma propriedade com lógica de acessador get
e set
para lidar com entradas inválidas.A associação de dados funciona com uma única cadeia de caracteres de formato DateTime usando @bind:format="{FORMAT STRING}"
, em que o espaço reservado {FORMAT STRING}
é a cadeia de caracteres de formato. Outras expressões de formato, como formatos de moeda ou número, não estão disponíveis no momento, mas poderão ser adicionadas em uma versão futura.
DateBinding.razor
:
@page "/date-binding"
<PageTitle>Date Binding</PageTitle>
<h1>Date Binding Example</h1>
<p>
<label>
<code>yyyy-MM-dd</code> format:
<input @bind="startDate" @bind:format="yyyy-MM-dd" />
</label>
</p>
<p>
<code>startDate</code>: @startDate
</p>
@code {
private DateTime startDate = new(2020, 1, 1);
}
@page "/date-binding"
<PageTitle>Date Binding</PageTitle>
<h1>Date Binding Example</h1>
<p>
<label>
<code>yyyy-MM-dd</code> format:
<input @bind="startDate" @bind:format="yyyy-MM-dd" />
</label>
</p>
<p>
<code>startDate</code>: @startDate
</p>
@code {
private DateTime startDate = new(2020, 1, 1);
}
@page "/date-binding"
<p>
<label>
<code>yyyy-MM-dd</code> format:
<input @bind="startDate" @bind:format="yyyy-MM-dd" />
</label>
</p>
<p>
<code>startDate</code>: @startDate
</p>
@code {
private DateTime startDate = new(2020, 1, 1);
}
@page "/date-binding"
<p>
<label>
<code>yyyy-MM-dd</code> format:
<input @bind="startDate" @bind:format="yyyy-MM-dd" />
</label>
</p>
<p>
<code>startDate</code>: @startDate
</p>
@code {
private DateTime startDate = new(2020, 1, 1);
}
@page "/date-binding"
<p>
<label>
<code>yyyy-MM-dd</code> format:
<input @bind="startDate" @bind:format="yyyy-MM-dd" />
</label>
</p>
<p>
<code>startDate</code>: @startDate
</p>
@code {
private DateTime startDate = new(2020, 1, 1);
}
@page "/date-binding"
<p>
<label>
<code>yyyy-MM-dd</code> format:
<input @bind="startDate" @bind:format="yyyy-MM-dd" />
</label>
</p>
<p>
<code>startDate</code>: @startDate
</p>
@code {
private DateTime startDate = new DateTime(2020, 1, 1);
}
No código anterior, o tipo de campo do elemento <input>
(atributo type
) assume o padrão de text
.
System.DateTime e System.DateTimeOffset anuláveis têm suporte:
private DateTime? date;
private DateTimeOffset? dateOffset;
Não é recomendável especificar um formato para o tipo de campo date
porque o Blazor tem suporte interno à formatação de datas. Apesar da recomendação, use apenas o formato de data yyyy-MM-dd
para associar à função corretamente se um formato for fornecido com o tipo de campo date
:
<input type="date" @bind="startDate" @bind:format="yyyy-MM-dd">
Um cenário comum é associar uma propriedade de um componente filho a uma propriedade em seu componente pai. Esse cenário é chamado de associação em cadeia porque vários níveis de associação ocorrem simultaneamente.
Você não pode implementar associações encadeadas com sintaxe @bind
em um componente filho. Um manipulador de eventos e um valor precisam ser especificados separadamente para dar suporte à atualização da propriedade no pai do componente filho. O componente pai ainda aproveita a sintaxe @bind
para configurar a associação de dados com o componente filho.
O componente ChildBind
a seguir tem um parâmetro de componente Year
e um EventCallback<TValue>. Por convenção, o EventCallback<TValue> para o parâmetro precisa ser nomeado como o nome do parâmetro de componente com um sufixo "Changed
". A sintaxe de nomenclatura é {PARAMETER NAME}Changed
, em que o espaço reservado {PARAMETER NAME}
é o nome do parâmetro. No exemplo a seguir, o EventCallback<TValue> tem o nome de YearChanged
.
EventCallback.InvokeAsync invoca o delegado referente à associação com o argumento fornecido e envia uma notificação de evento para a propriedade alterada.
ChildBind.razor
:
<div class="card bg-light mt-3" style="width:18rem ">
<div class="card-body">
<h3 class="card-title">ChildBind Component</h3>
<p class="card-text">
Child <code>Year</code>: @Year
</p>
<button @onclick="UpdateYearFromChild">Update Year from Child</button>
</div>
</div>
@code {
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
private async Task UpdateYearFromChild() =>
await YearChanged.InvokeAsync(Random.Shared.Next(1950, 2021));
}
<div class="card bg-light mt-3" style="width:18rem ">
<div class="card-body">
<h3 class="card-title">ChildBind Component</h3>
<p class="card-text">
Child <code>Year</code>: @Year
</p>
<button @onclick="UpdateYearFromChild">Update Year from Child</button>
</div>
</div>
@code {
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
private async Task UpdateYearFromChild() =>
await YearChanged.InvokeAsync(Random.Shared.Next(1950, 2021));
}
<div class="card bg-light mt-3" style="width:18rem ">
<div class="card-body">
<h3 class="card-title">ChildBind Component</h3>
<p class="card-text">
Child <code>Year</code>: @Year
</p>
<button @onclick="UpdateYearFromChild">Update Year from Child</button>
</div>
</div>
@code {
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
private async Task UpdateYearFromChild()
{
await YearChanged.InvokeAsync(Random.Shared.Next(1950, 2021));
}
}
<div class="card bg-light mt-3" style="width:18rem ">
<div class="card-body">
<h3 class="card-title">ChildBind Component</h3>
<p class="card-text">
Child <code>Year</code>: @Year
</p>
<button @onclick="UpdateYearFromChild">Update Year from Child</button>
</div>
</div>
@code {
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
private async Task UpdateYearFromChild()
{
await YearChanged.InvokeAsync(Random.Shared.Next(1950, 2021));
}
}
<div class="card bg-light mt-3" style="width:18rem ">
<div class="card-body">
<h3 class="card-title">ChildBind Component</h3>
<p class="card-text">
Child <code>Year</code>: @Year
</p>
<button @onclick="UpdateYearFromChild">Update Year from Child</button>
</div>
</div>
@code {
private Random r = new();
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
private async Task UpdateYearFromChild()
{
await YearChanged.InvokeAsync(r.Next(1950, 2021));
}
}
<div class="card bg-light mt-3" style="width:18rem ">
<div class="card-body">
<h3 class="card-title">ChildBind Component</h3>
<p class="card-text">
Child <code>Year</code>: @Year
</p>
<button @onclick="UpdateYearFromChild">Update Year from Child</button>
</div>
</div>
@code {
private Random r = new Random();
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
private async Task UpdateYearFromChild()
{
await YearChanged.InvokeAsync(r.Next(1950, 2021));
}
}
Para obter mais informações sobre eventos e EventCallback<TValue>, confira a seção EventCallback do artigo Tratamento de eventos do Blazor do ASP.NET Core .
No componente Parent1
a seguir, o campo year
está associado ao parâmetro Year
do componente filho. O parâmetro Year
é associável porque tem um evento YearChanged
complementar que corresponde ao tipo do parâmetro Year
.
Parent1.razor
:
@page "/parent-1"
<PageTitle>Parent 1</PageTitle>
<h1>Parent Example 1</h1>
<p>Parent <code>year</code>: @year</p>
<button @onclick="UpdateYear">Update Parent <code>year</code></button>
<ChildBind @bind-Year="year" />
@code {
private int year = 1979;
private void UpdateYear() => year = Random.Shared.Next(1950, 2021);
}
@page "/parent-1"
<PageTitle>Parent 1</PageTitle>
<h1>Parent Example 1</h1>
<p>Parent <code>year</code>: @year</p>
<button @onclick="UpdateYear">Update Parent <code>year</code></button>
<ChildBind @bind-Year="year" />
@code {
private int year = 1979;
private void UpdateYear() => year = Random.Shared.Next(1950, 2021);
}
@page "/parent-1"
<h1>Parent Component</h1>
<p>Parent <code>year</code>: @year</p>
<button @onclick="UpdateYear">Update Parent <code>year</code></button>
<ChildBind @bind-Year="year" />
@code {
private int year = 1979;
private void UpdateYear()
{
year = Random.Shared.Next(1950, 2021);
}
}
@page "/parent-1"
<h1>Parent Component</h1>
<p>Parent <code>year</code>: @year</p>
<button @onclick="UpdateYear">Update Parent <code>year</code></button>
<ChildBind @bind-Year="year" />
@code {
private int year = 1979;
private void UpdateYear()
{
year = Random.Shared.Next(1950, 2021);
}
}
@page "/parent-1"
<h1>Parent Component</h1>
<p>Parent <code>year</code>: @year</p>
<button @onclick="UpdateYear">Update Parent <code>year</code></button>
<ChildBind @bind-Year="year" />
@code {
private Random r = new();
private int year = 1979;
private void UpdateYear()
{
year = r.Next(1950, 2021);
}
}
@page "/parent-1"
<h1>Parent Component</h1>
<p>Parent <code>year</code>: @year</p>
<button @onclick="UpdateYear">Update Parent <code>year</code></button>
<ChildBind @bind-Year="year" />
@code {
private Random r = new Random();
private int year = 1979;
private void UpdateYear()
{
year = r.Next(1950, 2021);
}
}
A associação de parâmetro de componente também pode disparar eventos @bind:after
. No exemplo a seguir, o método YearUpdated
é executado de forma assíncrona depois de associar o parâmetro de componente Year
.
<ChildBind @bind-Year="year" @bind-Year:after="YearUpdated" />
@code {
...
private async Task YearUpdated()
{
... = await ...;
}
}
Por convenção, uma propriedade pode ser associada a um manipulador de eventos correspondente, incluindo um atributo @bind-{PROPERTY}:event
atribuído ao manipulador, em que o espaço reservado {PROPERTY}
é a propriedade. <ChildBind @bind-Year="year" />
é equivalente à gravação de:
<ChildBind @bind-Year="year" @bind-Year:event="YearChanged" />
Em um exemplo mais sofisticado e real, o seguinte componente PasswordEntry
:
<input>
como um campo password
.Password
a um componente pai com um EventCallback
que transmite o valor atual do campo password
filho como seu argumento.onclick
para disparar o método ToggleShowPassword
. Para saber mais, confira Tratamento de eventos do Blazor no ASP.NET Core.Aviso
Não armazene segredos de aplicativo, cadeias de conexão, credenciais, senhas, PINs (números de identificação pessoal), código C#/.NET privado ou chaves/tokens privados no código do lado do cliente, que é sempre inseguro. Em ambientes de teste/preparo e produção, o código do lado do Blazor servidor e as APIs Web devem usar fluxos de autenticação seguros que evitam a manutenção de credenciais no código do projeto ou nos arquivos de configuração. Fora dos testes de desenvolvimento local, recomendamos evitar o uso de variáveis de ambiente para armazenar dados confidenciais, pois as variáveis de ambiente não são a abordagem mais segura. Para testes de desenvolvimento local, a ferramenta Gerenciador de segredos é recomendada para proteger dados confidenciais. Para obter mais informações, consulte Manter dados e credenciais confidenciais com segurança.
PasswordEntry.razor
:
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string? password;
[Parameter]
public string? Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private async Task OnPasswordChanged(ChangeEventArgs e)
{
password = e?.Value?.ToString();
await PasswordChanged.InvokeAsync(password);
}
private void ToggleShowPassword() => showPassword = !showPassword;
}
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string? password;
[Parameter]
public string? Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private async Task OnPasswordChanged(ChangeEventArgs e)
{
password = e?.Value?.ToString();
await PasswordChanged.InvokeAsync(password);
}
private void ToggleShowPassword() => showPassword = !showPassword;
}
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string? password;
[Parameter]
public string? Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private async Task OnPasswordChanged(ChangeEventArgs e)
{
password = e?.Value?.ToString();
await PasswordChanged.InvokeAsync(password);
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string? password;
[Parameter]
public string? Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private async Task OnPasswordChanged(ChangeEventArgs e)
{
password = e?.Value?.ToString();
await PasswordChanged.InvokeAsync(password);
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string password;
[Parameter]
public string Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private async Task OnPasswordChanged(ChangeEventArgs e)
{
password = e.Value.ToString();
await PasswordChanged.InvokeAsync(password);
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string password;
[Parameter]
public string Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private async Task OnPasswordChanged(ChangeEventArgs e)
{
password = e.Value.ToString();
await PasswordChanged.InvokeAsync(password);
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
O componente PasswordEntry
é usado em outro componente, por exemplo, o exemplo de componente PasswordBinding
a seguir.
PasswordBinding.razor
:
@page "/password-binding"
<PageTitle>Password Binding</PageTitle>
<h1>Password Binding Example</h1>
<PasswordEntry @bind-Password="password" />
<p>
<code>password</code>: @password
</p>
@code {
private string password = "Not set";
}
@page "/password-binding"
<PageTitle>Password Binding</PageTitle>
<h1>Password Binding Example</h1>
<PasswordEntry @bind-Password="password" />
<p>
<code>password</code>: @password
</p>
@code {
private string password = "Not set";
}
@page "/password-binding"
<h1>Password Binding</h1>
<PasswordEntry @bind-Password="password" />
<p>
<code>password</code>: @password
</p>
@code {
private string password = "Not set";
}
@page "/password-binding"
<h1>Password Binding</h1>
<PasswordEntry @bind-Password="password" />
<p>
<code>password</code>: @password
</p>
@code {
private string password = "Not set";
}
@page "/password-binding"
<h1>Password Binding</h1>
<PasswordEntry @bind-Password="password" />
<p>
<code>password</code>: @password
</p>
@code {
private string password = "Not set";
}
@page "/password-binding"
<h1>Password Binding</h1>
<PasswordEntry @bind-Password="password" />
<p>
<code>password</code>: @password
</p>
@code {
private string password = "Not set";
}
Quando o componente PasswordBinding
é renderizado inicialmente, o valor password
de Not set
é exibido na interface do usuário. Após a renderização inicial, o valor de password
reflete as alterações feitas no valor do parâmetro de componente Password
no componente PasswordEntry
.
Nota
O exemplo anterior associa a senha unidirecional do componente PasswordEntry
filho ao componente PasswordBinding
pai. A associação bidirecional não será um requisito nesse cenário se a meta for que o aplicativo tenha um componente de entrada de senha compartilhada para reutilização no aplicativo que simplesmente transmite a senha ao pai. Para obter uma abordagem que permita a associação bidirecional sem gravar diretamente no parâmetro do componente filho, confira o exemplo de componente NestedChild
na seção Associar em mais de dois componentes deste artigo.
Execute verificações ou erros de interceptação no manipulador. O componente PasswordEntry
revisado a seguir fornece comentários imediatos ao usuário quando um espaço é usado no valor da senha.
PasswordEntry.razor
:
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string? password;
private string? validationMessage;
[Parameter]
public string? Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private Task OnPasswordChanged(ChangeEventArgs e)
{
password = e?.Value?.ToString();
if (password != null && password.Contains(' '))
{
validationMessage = "Spaces not allowed!";
return Task.CompletedTask;
}
else
{
validationMessage = string.Empty;
return PasswordChanged.InvokeAsync(password);
}
}
private void ToggleShowPassword() => showPassword = !showPassword;
}
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string? password;
private string? validationMessage;
[Parameter]
public string? Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private Task OnPasswordChanged(ChangeEventArgs e)
{
password = e?.Value?.ToString();
if (password != null && password.Contains(' '))
{
validationMessage = "Spaces not allowed!";
return Task.CompletedTask;
}
else
{
validationMessage = string.Empty;
return PasswordChanged.InvokeAsync(password);
}
}
private void ToggleShowPassword() => showPassword = !showPassword;
}
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string? password;
private string? validationMessage;
[Parameter]
public string? Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private Task OnPasswordChanged(ChangeEventArgs e)
{
password = e?.Value?.ToString();
if (password != null && password.Contains(' '))
{
validationMessage = "Spaces not allowed!";
return Task.CompletedTask;
}
else
{
validationMessage = string.Empty;
return PasswordChanged.InvokeAsync(password);
}
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
No exemplo abaixo, o método PasswordUpdated
é executado de forma assíncrona depois de associar o parâmetro de componente Password
:
<PasswordEntry @bind-Password="password" @bind-Password:after="PasswordUpdated" />
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string? password;
private string? validationMessage;
[Parameter]
public string? Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private Task OnPasswordChanged(ChangeEventArgs e)
{
password = e?.Value?.ToString();
if (password != null && password.Contains(' '))
{
validationMessage = "Spaces not allowed!";
return Task.CompletedTask;
}
else
{
validationMessage = string.Empty;
return PasswordChanged.InvokeAsync(password);
}
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string password;
private string validationMessage;
[Parameter]
public string Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private Task OnPasswordChanged(ChangeEventArgs e)
{
password = e.Value.ToString();
if (password.Contains(' '))
{
validationMessage = "Spaces not allowed!";
return Task.CompletedTask;
}
else
{
validationMessage = string.Empty;
return PasswordChanged.InvokeAsync(password);
}
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string password;
private string validationMessage;
[Parameter]
public string Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private Task OnPasswordChanged(ChangeEventArgs e)
{
password = e.Value.ToString();
if (password.Contains(' '))
{
validationMessage = "Spaces not allowed!";
return Task.CompletedTask;
}
else
{
validationMessage = string.Empty;
return PasswordChanged.InvokeAsync(password);
}
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
Você pode associar parâmetros por meio de vários componentes aninhados, mas precisa respeitar o fluxo unidirecional de dados:
Uma abordagem comum e recomendada é armazenar apenas os dados subjacentes no componente pai para evitar confusão sobre qual estado precisa ser atualizado, conforme mostrado no exemplo a seguir.
Parent2.razor
:
@page "/parent-2"
<PageTitle>Parent 2</PageTitle>
<h1>Parent Example 2</h1>
<p>Parent Message: <b>@parentMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Parent</button>
</p>
<NestedChild @bind-ChildMessage="parentMessage" />
@code {
private string parentMessage = "Initial value set in Parent";
private void ChangeValue() => parentMessage = $"Set in Parent {DateTime.Now}";
}
@page "/parent-2"
<PageTitle>Parent 2</PageTitle>
<h1>Parent Example 2</h1>
<p>Parent Message: <b>@parentMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Parent</button>
</p>
<NestedChild @bind-ChildMessage="parentMessage" />
@code {
private string parentMessage = "Initial value set in Parent";
private void ChangeValue() => parentMessage = $"Set in Parent {DateTime.Now}";
}
@page "/parent-2"
<h1>Parent Component</h1>
<p>Parent Message: <b>@parentMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Parent</button>
</p>
<NestedChild @bind-ChildMessage="parentMessage" />
@code {
private string parentMessage = "Initial value set in Parent";
private void ChangeValue()
{
parentMessage = $"Set in Parent {DateTime.Now}";
}
}
@page "/parent-2"
<h1>Parent Component</h1>
<p>Parent Message: <b>@parentMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Parent</button>
</p>
<NestedChild @bind-ChildMessage="parentMessage" />
@code {
private string parentMessage = "Initial value set in Parent";
private void ChangeValue()
{
parentMessage = $"Set in Parent {DateTime.Now}";
}
}
@page "/parent-2"
<h1>Parent Component</h1>
<p>Parent Message: <b>@parentMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Parent</button>
</p>
<NestedChild @bind-ChildMessage="parentMessage" />
@code {
private string parentMessage = "Initial value set in Parent";
private void ChangeValue()
{
parentMessage = $"Set in Parent {DateTime.Now}";
}
}
@page "/parent-2"
<h1>Parent Component</h1>
<p>Parent Message: <b>@parentMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Parent</button>
</p>
<NestedChild @bind-ChildMessage="parentMessage" />
@code {
private string parentMessage = "Initial value set in Parent";
private void ChangeValue()
{
parentMessage = $"Set in Parent {DateTime.Now}";
}
}
No seguinte componente NestedChild
, o componente NestedGrandchild
:
ChildMessage
a GrandchildMessage
com sintaxe@bind:get
.GrandchildMessage
quando ChildMessageChanged
é executado com sintaxe @bind:set
.NestedChild.razor
:
<div class="border rounded m-1 p-1">
<h2>Child Component</h2>
<p>Child Message: <b>@ChildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Child</button>
</p>
<NestedGrandchild @bind-GrandchildMessage:get="ChildMessage"
@bind-GrandchildMessage:set="ChildMessageChanged" />
</div>
@code {
[Parameter]
public string? ChildMessage { get; set; }
[Parameter]
public EventCallback<string?> ChildMessageChanged { get; set; }
private async Task ChangeValue() =>
await ChildMessageChanged.InvokeAsync($"Set in Child {DateTime.Now}");
}
<div class="border rounded m-1 p-1">
<h2>Child Component</h2>
<p>Child Message: <b>@ChildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Child</button>
</p>
<NestedGrandchild @bind-GrandchildMessage:get="ChildMessage"
@bind-GrandchildMessage:set="ChildMessageChanged" />
</div>
@code {
[Parameter]
public string? ChildMessage { get; set; }
[Parameter]
public EventCallback<string?> ChildMessageChanged { get; set; }
private async Task ChangeValue() =>
await ChildMessageChanged.InvokeAsync($"Set in Child {DateTime.Now}");
}
<div class="border rounded m-1 p-1">
<h2>Child Component</h2>
<p>Child Message: <b>@ChildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Child</button>
</p>
<NestedGrandchild @bind-GrandchildMessage:get="ChildMessage"
@bind-GrandchildMessage:set="ChildMessageChanged" />
</div>
@code {
[Parameter]
public string? ChildMessage { get; set; }
[Parameter]
public EventCallback<string> ChildMessageChanged { get; set; }
private async Task ChangeValue()
{
await ChildMessageChanged.InvokeAsync(
$"Set in Child {DateTime.Now}");
}
}
<div class="border rounded m-1 p-1">
<h2>Child Component</h2>
<p>Child Message: <b>@ChildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Child</button>
</p>
<NestedGrandchild @bind-GrandchildMessage="BoundValue" />
</div>
@code {
[Parameter]
public string? ChildMessage { get; set; }
[Parameter]
public EventCallback<string> ChildMessageChanged { get; set; }
private string BoundValue
{
get => ChildMessage ?? string.Empty;
set => ChildMessageChanged.InvokeAsync(value);
}
private async Task ChangeValue()
{
await ChildMessageChanged.InvokeAsync(
$"Set in Child {DateTime.Now}");
}
}
<div class="border rounded m-1 p-1">
<h2>Child Component</h2>
<p>Child Message: <b>@ChildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Child</button>
</p>
<NestedGrandchild @bind-GrandchildMessage="BoundValue" />
</div>
@code {
[Parameter]
public string ChildMessage { get; set; }
[Parameter]
public EventCallback<string> ChildMessageChanged { get; set; }
private string BoundValue
{
get => ChildMessage;
set => ChildMessageChanged.InvokeAsync(value);
}
private async Task ChangeValue()
{
await ChildMessageChanged.InvokeAsync(
$"Set in Child {DateTime.Now}");
}
}
<div class="border rounded m-1 p-1">
<h2>Child Component</h2>
<p>Child Message: <b>@ChildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Child</button>
</p>
<NestedGrandchild @bind-GrandchildMessage="BoundValue" />
</div>
@code {
[Parameter]
public string ChildMessage { get; set; }
[Parameter]
public EventCallback<string> ChildMessageChanged { get; set; }
private string BoundValue
{
get => ChildMessage;
set => ChildMessageChanged.InvokeAsync(value);
}
private async Task ChangeValue()
{
await ChildMessageChanged.InvokeAsync(
$"Set in Child {DateTime.Now}");
}
}
Aviso
Em geral, evite criar componentes que gravam diretamente em seus próprios parâmetros de componente. O componente NestedChild
anterior usa uma propriedade BoundValue
em vez de gravar diretamente em seu parâmetro ChildMessage
. Para obter mais informações, confira Evitar a substituição de parâmetros no ASP.NET Core Blazor.
NestedGrandchild.razor
:
<div class="border rounded m-1 p-1">
<h3>Grandchild Component</h3>
<p>Grandchild Message: <b>@GrandchildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Grandchild</button>
</p>
</div>
@code {
[Parameter]
public string? GrandchildMessage { get; set; }
[Parameter]
public EventCallback<string> GrandchildMessageChanged { get; set; }
private async Task ChangeValue() =>
await GrandchildMessageChanged.InvokeAsync(
$"Set in Grandchild {DateTime.Now}");
}
<div class="border rounded m-1 p-1">
<h3>Grandchild Component</h3>
<p>Grandchild Message: <b>@GrandchildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Grandchild</button>
</p>
</div>
@code {
[Parameter]
public string? GrandchildMessage { get; set; }
[Parameter]
public EventCallback<string> GrandchildMessageChanged { get; set; }
private async Task ChangeValue() =>
await GrandchildMessageChanged.InvokeAsync(
$"Set in Grandchild {DateTime.Now}");
}
<div class="border rounded m-1 p-1">
<h3>Grandchild Component</h3>
<p>Grandchild Message: <b>@GrandchildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Grandchild</button>
</p>
</div>
@code {
[Parameter]
public string? GrandchildMessage { get; set; }
[Parameter]
public EventCallback<string> GrandchildMessageChanged { get; set; }
private async Task ChangeValue()
{
await GrandchildMessageChanged.InvokeAsync(
$"Set in Grandchild {DateTime.Now}");
}
}
<div class="border rounded m-1 p-1">
<h3>Grandchild Component</h3>
<p>Grandchild Message: <b>@GrandchildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Grandchild</button>
</p>
</div>
@code {
[Parameter]
public string? GrandchildMessage { get; set; }
[Parameter]
public EventCallback<string> GrandchildMessageChanged { get; set; }
private async Task ChangeValue()
{
await GrandchildMessageChanged.InvokeAsync(
$"Set in Grandchild {DateTime.Now}");
}
}
<div class="border rounded m-1 p-1">
<h3>Grandchild Component</h3>
<p>Grandchild Message: <b>@GrandchildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Grandchild</button>
</p>
</div>
@code {
[Parameter]
public string GrandchildMessage { get; set; }
[Parameter]
public EventCallback<string> GrandchildMessageChanged { get; set; }
private async Task ChangeValue()
{
await GrandchildMessageChanged.InvokeAsync(
$"Set in Grandchild {DateTime.Now}");
}
}
<div class="border rounded m-1 p-1">
<h3>Grandchild Component</h3>
<p>Grandchild Message: <b>@GrandchildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Grandchild</button>
</p>
</div>
@code {
[Parameter]
public string GrandchildMessage { get; set; }
[Parameter]
public EventCallback<string> GrandchildMessageChanged { get; set; }
private async Task ChangeValue()
{
await GrandchildMessageChanged.InvokeAsync(
$"Set in Grandchild {DateTime.Now}");
}
}
Para obter uma abordagem alternativa adequada para compartilhar dados na memória e entre componentes que não estão necessariamente aninhados, confira Gerenciamento de estado do Blazor do ASP.NET Core.
Para facilitar interações mais profundas com uma associação, Blazor permite a você capturar da árvore de expressão de um campo ou propriedade associado. Isso é obtido definindo uma propriedade com o nome do campo ou da propriedade sufixo com Expression
. Para qualquer campo ou propriedade nomeada {FIELD OR PROPERTY NAME}
, a propriedade de árvore de expressão correspondente é nomeada {FIELD OR PROPERTY NAME}Expression
.
O componente ChildParameterExpression
a seguir identifica o modelo e o nome do campo da expressão Year
. Um FieldIdentifier, que é usado para obter o modelo e o nome do campo, identifica exclusivamente um único campo que pode ser editado. Isso pode corresponder a uma propriedade em um objeto de modelo ou pode ser qualquer outro valor nomeado. O uso da expressão de um parâmetro é útil ao criar componentes de validação personalizados, que não são cobertos pela documentação do Microsoft Blazor, mas são abordados por vários recursos de terceiros.
ChildParameterExpression.razor
:
@using System.Linq.Expressions
<ul>
<li>Year model: @yearField.Model</li>
<li>Year field name: @yearField.FieldName</li>
</ul>
@code {
private FieldIdentifier yearField;
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
[Parameter]
public Expression<Func<int>> YearExpression { get; set; } = default!;
protected override void OnInitialized() =>
yearField = FieldIdentifier.Create(YearExpression);
}
@using System.Linq.Expressions
<ul>
<li>Year model: @yearField.Model</li>
<li>Year field name: @yearField.FieldName</li>
</ul>
@code {
private FieldIdentifier yearField;
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
[Parameter]
public Expression<Func<int>> YearExpression { get; set; } = default!;
protected override void OnInitialized() =>
yearField = FieldIdentifier.Create(YearExpression);
}
@using System.Linq.Expressions
<ul>
<li>Year model: @yearField.Model</li>
<li>Year field name: @yearField.FieldName</li>
</ul>
@code {
private FieldIdentifier yearField;
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
[Parameter]
public Expression<Func<int>> YearExpression { get; set; } = default!;
protected override void OnInitialized()
{
yearField = FieldIdentifier.Create(YearExpression);
}
}
@using System.Linq.Expressions
<ul>
<li>Year model: @yearField.Model</li>
<li>Year field name: @yearField.FieldName</li>
</ul>
@code {
private FieldIdentifier yearField;
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
[Parameter]
public Expression<Func<int>> YearExpression { get; set; } = default!;
protected override void OnInitialized()
{
yearField = FieldIdentifier.Create(YearExpression);
}
}
@using System.Linq.Expressions
<ul>
<li>Year model: @yearField.Model</li>
<li>Year field name: @yearField.FieldName</li>
</ul>
@code {
private FieldIdentifier yearField;
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
[Parameter]
public Expression<Func<int>> YearExpression { get; set; } = default!;
protected override void OnInitialized()
{
yearField = FieldIdentifier.Create(YearExpression);
}
}
@using System.Linq.Expressions
<ul>
<li>Year model: @yearField.Model</li>
<li>Year field name: @yearField.FieldName</li>
</ul>
@code {
private FieldIdentifier yearField;
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
[Parameter]
public Expression<Func<int>> YearExpression { get; set; } = default!;
protected override void OnInitialized()
{
yearField = FieldIdentifier.Create(YearExpression);
}
}
Parent3.razor
:
@page "/parent-3"
<PageTitle>Parent 3</PageTitle>
<h1>Parent Example 3</h1>
<p>Parent <code>year</code>: @year</p>
<ChildParameterExpression @bind-Year="year" />
@code {
private int year = 1979;
}
@page "/parent-3"
<PageTitle>Parent 3</PageTitle>
<h1>Parent Example 3</h1>
<p>Parent <code>year</code>: @year</p>
<ChildParameterExpression @bind-Year="year" />
@code {
private int year = 1979;
}
@page "/parent-3"
<h1>Parent Example 3</h1>
<p>Parent <code>year</code>: @year</p>
<ChildParameterExpression @bind-Year="year" />
@code {
private int year = 1979;
}
@page "/parent-3"
<h1>Parent Example 3</h1>
<p>Parent <code>year</code>: @year</p>
<ChildParameterExpression @bind-Year="year" />
@code {
private int year = 1979;
}
@page "/parent-3"
<h1>Parent Example 3</h1>
<p>Parent <code>year</code>: @year</p>
<ChildParameterExpression @bind-Year="year" />
@code {
private int year = 1979;
}
@page "/parent-3"
<h1>Parent Example 3</h1>
<p>Parent <code>year</code>: @year</p>
<ChildParameterExpression @bind-Year="year" />
@code {
private int year = 1979;
}
InputSelect
a valores de objeto C# null
EventCallback
seçãodotnet/blazor-samples
) (como baixar)Comentários do ASP.NET Core
O ASP.NET Core é um projeto código aberto. Selecione um link para fornecer comentários:
evento
19/11, 23 - 21/11, 23
Participe de sessões on-line no Microsoft Ignite criadas para expandir suas habilidades e ajudá-lo a lidar com os problemas complexos de hoje.
Registe-se agora