Crear y usar ASP.NET Core componentes de RazorCreate and use ASP.NET Core Razor components

Por Luke Latham y Daniel RothBy Luke Latham and Daniel Roth

Vea o descargue el código de ejemplo (cómo descargarlo)View or download sample code (how to download)

Blazor aplicaciones se compilan con componentesde. apps are built using components. Un componente es un fragmento independiente de la interfaz de usuario (IU), como una página, un cuadro de diálogo o un formulario.A component is a self-contained chunk of user interface (UI), such as a page, dialog, or form. Un componente incluye el marcado HTML y la lógica de procesamiento necesaria para insertar datos o responder a los eventos de la interfaz de usuario.A component includes HTML markup and the processing logic required to inject data or respond to UI events. Los componentes son flexibles y ligeros.Components are flexible and lightweight. Se pueden anidar, reutilizar y compartir entre proyectos.They can be nested, reused, and shared among projects.

Clases de componentesComponent classes

Los componentes se implementan en archivos de componentes de Razor ( . Razor) C# mediante una combinación de y el marcado HTML.Components are implemented in Razor component files (.razor) using a combination of C# and HTML markup. Un componente de Blazor se conoce formalmente como un componente de Razor.A component in Blazor is formally referred to as a Razor component.

El nombre de un componente debe empezar con un carácter en mayúsculas.A component's name must start with an uppercase character. Por ejemplo, MyCoolComponent. Razor es válido y MyCoolComponent. Razor no es válido.For example, MyCoolComponent.razor is valid, and myCoolComponent.razor is invalid.

La interfaz de usuario de un componente se define mediante HTML.The UI for a component is defined using HTML. La lógica de la representación dinámica (por ejemplo, bucles, instrucciones condicionales, expresiones) se agrega mediante una sintaxis de C# insertada denominada Razor.Dynamic rendering logic (for example, loops, conditionals, expressions) is added using an embedded C# syntax called Razor. Cuando se compila una aplicación, el marcado HTML y C# la lógica de representación se convierten en una clase de componente.When an app is compiled, the HTML markup and C# rendering logic are converted into a component class. El nombre de la clase generada coincide con el nombre del archivo.The name of the generated class matches the name of the file.

Los miembros de la clase de componente se definen en un bloque @code.Members of the component class are defined in an @code block. En el bloque @code, el estado de componente (propiedades, campos) se especifica con métodos para el control de eventos o para definir otra lógica de componentes.In the @code block, component state (properties, fields) is specified with methods for event handling or for defining other component logic. Se permite emplear más de un bloque @code.More than one @code block is permissible.

Nota

En las versiones preliminares anteriores de ASP.NET Core 3,0, @functions bloques se usaban para el mismo propósito que @code bloques en los componentes de Razor.In prior previews of ASP.NET Core 3.0, @functions blocks were used for the same purpose as @code blocks in Razor components. @functions bloques continúan funcionando en los componentes de Razor, pero se recomienda usar el bloque de @code en ASP.NET Core 3,0 Preview 6 o posterior.@functions blocks continue to function in Razor components, but we recommend using the @code block in ASP.NET Core 3.0 Preview 6 or later.

Los miembros de componente se pueden usar como parte de la lógica de representación del C# componente mediante expresiones que comienzan con @.Component members can be used as part of the component's rendering logic using C# expressions that start with @. Por ejemplo, un C# campo se representa mediante el prefijo @ al nombre del campo.For example, a C# field is rendered by prefixing @ to the field name. En el siguiente ejemplo se evalúa y representa:The following example evaluates and renders:

  • _headingFontStyle al valor de la propiedad CSS para font-style._headingFontStyle to the CSS property value for font-style.
  • _headingText al contenido del elemento <h1>._headingText to the content of the <h1> element.
<h1 style="font-style:@_headingFontStyle">@_headingText</h1>

@code {
    private string _headingFontStyle = "italic";
    private string _headingText = "Put on your new Blazor!";
}

Una vez que el componente se representa inicialmente, el componente regenera su árbol de representación en respuesta a los eventos.After the component is initially rendered, the component regenerates its render tree in response to events. a continuación, Blazor compara el nuevo árbol de representación con el anterior y aplica cualquier modificación a la Document Object Model del explorador (DOM).Blazor then compares the new render tree against the previous one and applies any modifications to the browser's Document Object Model (DOM).

Los componentes son C# clases normales y se pueden colocar en cualquier parte dentro de un proyecto.Components are ordinary C# classes and can be placed anywhere within a project. Los componentes que generan páginas web normalmente residen en la carpeta pages .Components that produce webpages usually reside in the Pages folder. Los componentes que no son de página se colocan con frecuencia en la carpeta compartida o en una carpeta personalizada agregada al proyecto.Non-page components are frequently placed in the Shared folder or a custom folder added to the project. Para usar una carpeta personalizada, agregue el espacio de nombres de la carpeta personalizada al componente primario o al archivo _Imports. Razor de la aplicación.To use a custom folder, add the custom folder's namespace to either the parent component or to the app's _Imports.razor file. Por ejemplo, el siguiente espacio de nombres hace que los componentes de una carpeta Components estén disponibles cuando se WebApplicationel espacio de nombres raíz de la aplicación:For example, the following namespace makes components in a Components folder available when the app's root namespace is WebApplication:

@using WebApplication.Components

Integración de componentes en aplicaciones de Razor Pages y MVCIntegrate components into Razor Pages and MVC apps

Usar componentes con las aplicaciones de Razor Pages y MVC existentes.Use components with existing Razor Pages and MVC apps. No es necesario volver a escribir las páginas o vistas existentes para utilizar los componentes de Razor.There's no need to rewrite existing pages or views to use Razor components. Cuando se representa la página o la vista, los componentes se preprocesan al mismo tiempo.When the page or view is rendered, components are prerendered at the same time.

Para representar un componente de una página o vista, use la aplicación auxiliar de etiquetas Component:To render a component from a page or view, use the Component Tag Helper:

<component type="typeof(Counter)" render-mode="ServerPrerendered" 
    param-IncrementAmount="10" />

Se admite el paso de parámetros (por ejemplo, IncrementAmount en el ejemplo anterior).Passing parameters (for example, IncrementAmount in the preceding example) is supported.

RenderMode configura si el componente:RenderMode configures whether the component:

  • Se representa en la página.Is prerendered into the page.
  • Se representa como HTML estático en la página o si incluye la información necesaria para arrancar una aplicación Blazor desde el agente de usuario.Is rendered as static HTML on the page or if it includes the necessary information to bootstrap a Blazor app from the user agent.
RenderMode DescripciónDescription
ServerPrerendered Representa el componente en código HTML estático e incluye un marcador para una aplicación de Blazor Server.Renders the component into static HTML and includes a marker for a Blazor Server app. Cuando se inicia el agente de usuario, este marcador se usa para arrancar una aplicación Blazor.When the user-agent starts, this marker is used to bootstrap a Blazor app.
Server Representa un marcador para una aplicación de Blazor Server.Renders a marker for a Blazor Server app. La salida del componente no está incluida.Output from the component isn't included. Cuando se inicia el agente de usuario, este marcador se usa para arrancar una aplicación Blazor.When the user-agent starts, this marker is used to bootstrap a Blazor app.
Static Representa el componente en HTML estático.Renders the component into static HTML.

Mientras que las páginas y las vistas pueden utilizar componentes, el opuesto no es cierto.While pages and views can use components, the converse isn't true. Los componentes no pueden usar escenarios específicos de la página y de la vista, como vistas y secciones parciales.Components can't use view- and page-specific scenarios, such as partial views and sections. Para usar la lógica de la vista parcial en un componente, se debe factorizar la lógica de vista parcial en un componente.To use logic from partial view in a component, factor out the partial view logic into a component.

No se admite la representación de componentes de servidor desde una página HTML estática.Rendering server components from a static HTML page isn't supported.

Para obtener más información sobre cómo se representan los componentes, el estado del componente y la aplicación auxiliar de etiquetas Component, vea Modelos de hospedaje de Blazor de ASP.NET Core.For more information on how components are rendered, component state, and the Component Tag Helper, see Modelos de hospedaje de Blazor de ASP.NET Core.

Para representar un componente de una página o vista, use el método auxiliar HTML RenderComponentAsync<TComponent>:To render a component from a page or view, use the RenderComponentAsync<TComponent> HTML helper method:

@(await Html.RenderComponentAsync<MyComponent>(RenderMode.ServerPrerendered))

RenderMode configura si el componente:RenderMode configures whether the component:

  • Se representa en la página.Is prerendered into the page.
  • Se representa como HTML estático en la página o si incluye la información necesaria para arrancar una aplicación Blazor desde el agente de usuario.Is rendered as static HTML on the page or if it includes the necessary information to bootstrap a Blazor app from the user agent.
RenderMode DescripciónDescription
ServerPrerendered Representa el componente en código HTML estático e incluye un marcador para una aplicación de Blazor Server.Renders the component into static HTML and includes a marker for a Blazor Server app. Cuando se inicia el agente de usuario, este marcador se usa para arrancar una aplicación Blazor.When the user-agent starts, this marker is used to bootstrap a Blazor app. No se admiten los parámetros.Parameters aren't supported.
Server Representa un marcador para una aplicación de Blazor Server.Renders a marker for a Blazor Server app. La salida del componente no está incluida.Output from the component isn't included. Cuando se inicia el agente de usuario, este marcador se usa para arrancar una aplicación Blazor.When the user-agent starts, this marker is used to bootstrap a Blazor app. No se admiten los parámetros.Parameters aren't supported.
Static Representa el componente en HTML estático.Renders the component into static HTML. Se admiten los parámetros.Parameters are supported.

Mientras que las páginas y las vistas pueden utilizar componentes, el opuesto no es cierto.While pages and views can use components, the converse isn't true. Los componentes no pueden usar escenarios específicos de la página y de la vista, como vistas y secciones parciales.Components can't use view- and page-specific scenarios, such as partial views and sections. Para usar la lógica de la vista parcial en un componente, se debe factorizar la lógica de vista parcial en un componente.To use logic from partial view in a component, factor out the partial view logic into a component.

No se admite la representación de componentes de servidor desde una página HTML estática.Rendering server components from a static HTML page isn't supported.

Para obtener más información sobre cómo se representan los componentes, el estado del componente y la aplicación auxiliar HTML RenderComponentAsync, vea Modelos de hospedaje de Blazor de ASP.NET Core.For more information on how components are rendered, component state, and the RenderComponentAsync HTML Helper, see Modelos de hospedaje de Blazor de ASP.NET Core.

Uso de componentesUse components

Los componentes pueden incluir otros componentes declarándolos mediante la sintaxis de elementos HTML.Components can include other components by declaring them using HTML element syntax. El marcado para utilizar un componente se parece a una etiqueta HTML en la que el nombre de la etiqueta es el tipo de componente.The markup for using a component looks like an HTML tag where the name of the tag is the component type.

El enlace de atributo distingue mayúsculas de minúsculas.Attribute binding is case sensitive. Por ejemplo, @bind es válido y @Bind no es válido.For example, @bind is valid, and @Bind is invalid.

El marcado siguiente en index. Razor representa una instancia de HeadingComponent:The following markup in Index.razor renders a HeadingComponent instance:

<HeadingComponent />

Components/HeadingComponent. Razor:Components/HeadingComponent.razor:

@using System.Globalization
@*
    The 'using' directive makes System.Globalization available to 
    the component. System.Globalization provides a method for 
    converting a string into title case (capitalizes the first 
    letter of every word in a string), which is used to convert a 
    a string into title case for a heading.
*@

@*
    Heading text is rendered by evaluating the _headingText field. 
    The font-style of the heading is rendered by evaluating the 
    _headingFontStyle field.
*@
<h1 style="font-style:@_headingFontStyle">@_headingText</h1>

<form>
    <div>
        @*
            A check box sets the font style and is bound to the 
            _italicsCheck field.
        *@
        <input type="checkbox" id="italicsCheck" 
               @bind="_italicsCheck" />
        <label class="form-check-label" 
            for="italicsCheck">Use italics</label>
    </div>

    @*
        When the form is submitted, the onclick event executes 
        the UpdateHeading method.
    *@
    <button type="button" class="btn btn-primary" @onclick="UpdateHeading">
        Update heading
    </button>
</form>

@code {
    private static TextInfo _tinfo = CultureInfo.CurrentCulture.TextInfo;
    private string _headingText = 
        _tinfo.ToTitleCase("welcome to blazor!");
    private string _headingFontStyle = "normal";
    private bool _italicsCheck = false;

    // When UpdateHeading is executed, _italicsCheck determines 
    // the value of _headingFontStyle to set the font style of the 
    // heading.
    public void UpdateHeading()
    {
        _headingFontStyle = _italicsCheck ? "italic" : "normal";
    }
}

Si un componente contiene un elemento HTML con una primera letra mayúscula que no coincide con un nombre de componente, se emite una advertencia que indica que el elemento tiene un nombre inesperado.If a component contains an HTML element with an uppercase first letter that doesn't match a component name, a warning is emitted indicating that the element has an unexpected name. La adición de una instrucción @using para el espacio de nombres del componente hace que el componente esté disponible, lo que elimina la advertencia.Adding an @using statement for the component's namespace makes the component available, which removes the warning.

Parámetros del componenteComponent parameters

Los componentes pueden tener parámetros de componente, que se definen mediante propiedades públicas en la clase de componente con el atributo [Parameter].Components can have component parameters, which are defined using public properties on the component class with the [Parameter] attribute. Use atributos para especificar argumentos para un componente en el marcado.Use attributes to specify arguments for a component in markup.

Components/ChildComponent. Razor:Components/ChildComponent.razor:

<div class="panel panel-default">
    <div class="panel-heading">@Title</div>
    <div class="panel-body">@ChildContent</div>

    <button class="btn btn-primary" @onclick="OnClick">
        Trigger a Parent component method
    </button>
</div>

@code {
    [Parameter]
    public string Title { get; set; }

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    [Parameter]
    public EventCallback<MouseEventArgs> OnClick { get; set; }
}

En el ejemplo siguiente de la aplicación de ejemplo, el ParentComponent establece el valor de la propiedad Title de la ChildComponent.In the following example from the sample app, the ParentComponent sets the value of the Title property of the ChildComponent.

Pages/ParentComponent. Razor:Pages/ParentComponent.razor:

@page "/ParentComponent"

<h1>Parent-child example</h1>

<ChildComponent Title="Panel Title from Parent"
                OnClick="@ShowMessage">
    Content of the child component is supplied
    by the parent component.
</ChildComponent>

...

Contenido secundarioChild content

Los componentes pueden establecer el contenido de otro componente.Components can set the content of another component. El componente de asignación proporciona el contenido entre las etiquetas que especifican el componente receptor.The assigning component provides the content between the tags that specify the receiving component.

En el ejemplo siguiente, el ChildComponent tiene una propiedad ChildContent que representa un RenderFragment, que representa un segmento de interfaz de usuario que se va a representar.In the following example, the ChildComponent has a ChildContent property that represents a RenderFragment, which represents a segment of UI to render. El valor de ChildContent se coloca en el marcado del componente en el que se debe representar el contenido.The value of ChildContent is positioned in the component's markup where the content should be rendered. El valor de ChildContent se recibe del componente primario y se representa dentro del panel-bodydel panel de arranque.The value of ChildContent is received from the parent component and rendered inside the Bootstrap panel's panel-body.

Components/ChildComponent. Razor:Components/ChildComponent.razor:

<div class="panel panel-default">
    <div class="panel-heading">@Title</div>
    <div class="panel-body">@ChildContent</div>

    <button class="btn btn-primary" @onclick="OnClick">
        Trigger a Parent component method
    </button>
</div>

@code {
    [Parameter]
    public string Title { get; set; }

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    [Parameter]
    public EventCallback<MouseEventArgs> OnClick { get; set; }
}

Nota

La propiedad que recibe el contenido del RenderFragment debe denominarse ChildContent por Convención.The property receiving the RenderFragment content must be named ChildContent by convention.

El ParentComponent de la aplicación de ejemplo puede proporcionar contenido para representar el ChildComponent colocando el contenido dentro de las etiquetas de <ChildComponent>.The ParentComponent in the sample app can provide content for rendering the ChildComponent by placing the content inside the <ChildComponent> tags.

Pages/ParentComponent. Razor:Pages/ParentComponent.razor:

@page "/ParentComponent"

<h1>Parent-child example</h1>

<ChildComponent Title="Panel Title from Parent"
                OnClick="@ShowMessage">
    Content of the child component is supplied
    by the parent component.
</ChildComponent>

...

Atributos expansión y parámetros arbitrariosAttribute splatting and arbitrary parameters

Los componentes de pueden capturar y representar atributos adicionales además de los parámetros declarados del componente.Components can capture and render additional attributes in addition to the component's declared parameters. Los atributos adicionales se pueden capturar en un diccionario y, después, splatted en un elemento cuando el componente se representa mediante la Directiva de Razor @attributes .Additional attributes can be captured in a dictionary and then splatted onto an element when the component is rendered using the @attributes Razor directive. Este escenario es útil cuando se define un componente que genera un elemento de marcado que admite diversas personalizaciones.This scenario is useful when defining a component that produces a markup element that supports a variety of customizations. Por ejemplo, puede resultar tedioso definir atributos por separado para una <input> que admita muchos parámetros.For example, it can be tedious to define attributes separately for an <input> that supports many parameters.

En el ejemplo siguiente, el primer elemento <input> (id="useIndividualParams") utiliza parámetros de componente individuales, mientras que el segundo elemento <input> (id="useAttributesDict") utiliza el atributo expansión:In the following example, the first <input> element (id="useIndividualParams") uses individual component parameters, while the second <input> element (id="useAttributesDict") uses attribute splatting:

<input id="useIndividualParams"
       maxlength="@Maxlength"
       placeholder="@Placeholder"
       required="@Required"
       size="@Size" />

<input id="useAttributesDict"
       @attributes="InputAttributes" />

@code {
    [Parameter]
    public string Maxlength { get; set; } = "10";

    [Parameter]
    public string Placeholder { get; set; } = "Input placeholder text";

    [Parameter]
    public string Required { get; set; } = "required";

    [Parameter]
    public string Size { get; set; } = "50";

    [Parameter]
    public Dictionary<string, object> InputAttributes { get; set; } =
        new Dictionary<string, object>()
        {
            { "maxlength", "10" },
            { "placeholder", "Input placeholder text" },
            { "required", "required" },
            { "size", "50" }
        };
}

El tipo del parámetro debe implementar IEnumerable<KeyValuePair<string, object>> con claves de cadena.The type of the parameter must implement IEnumerable<KeyValuePair<string, object>> with string keys. El uso de IReadOnlyDictionary<string, object> también es una opción en este escenario.Using IReadOnlyDictionary<string, object> is also an option in this scenario.

Los elementos <input> representados con ambos enfoques son idénticos:The rendered <input> elements using both approaches is identical:

<input id="useIndividualParams"
       maxlength="10"
       placeholder="Input placeholder text"
       required="required"
       size="50">

<input id="useAttributesDict"
       maxlength="10"
       placeholder="Input placeholder text"
       required="required"
       size="50">

Para aceptar atributos arbitrarios, defina un parámetro de componente mediante el atributo [Parameter] con la propiedad CaptureUnmatchedValues establecida en true:To accept arbitrary attributes, define a component parameter using the [Parameter] attribute with the CaptureUnmatchedValues property set to true:

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object> InputAttributes { get; set; }
}

La propiedad CaptureUnmatchedValues en [Parameter] permite que el parámetro coincida con todos los atributos que no coinciden con ningún otro parámetro.The CaptureUnmatchedValues property on [Parameter] allows the parameter to match all attributes that don't match any other parameter. Un componente solo puede definir un parámetro con CaptureUnmatchedValues.A component can only define a single parameter with CaptureUnmatchedValues. El tipo de propiedad que se usa con CaptureUnmatchedValues se debe poder asignar desde Dictionary<string, object> con claves de cadena.The property type used with CaptureUnmatchedValues must be assignable from Dictionary<string, object> with string keys. IEnumerable<KeyValuePair<string, object>> o IReadOnlyDictionary<string, object> también son opciones en este escenario.IEnumerable<KeyValuePair<string, object>> or IReadOnlyDictionary<string, object> are also options in this scenario.

La posición de @attributes relativa a la posición de los atributos de elemento es importante.The position of @attributes relative to the position of element attributes is important. Cuando @attributes se splatted en el elemento, los atributos se procesan de derecha a izquierda (último a primero).When @attributes are splatted on the element, the attributes are processed from right to left (last to first). Considere el siguiente ejemplo de un componente que utiliza un componente de Child:Consider the following example of a component that consumes a Child component:

ParentComponent. Razor:ParentComponent.razor:

<ChildComponent extra="10" />

ChildComponent. Razor:ChildComponent.razor:

<div @attributes="AdditionalAttributes" extra="5" />

[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object> AdditionalAttributes { get; set; }

El atributo extra del componente de Child se establece a la derecha de @attributes.The Child component's extra attribute is set to the right of @attributes. El <div> representado del componente Parent contiene extra="5" cuando se pasa a través del atributo adicional, ya que los atributos se procesan de derecha a izquierda (último a primero):The Parent component's rendered <div> contains extra="5" when passed through the additional attribute because the attributes are processed right to left (last to first):

<div extra="5" />

En el ejemplo siguiente, el orden de extra y @attributes se invierte en el <div>del componente Child:In the following example, the order of extra and @attributes is reversed in the Child component's <div>:

ParentComponent. Razor:ParentComponent.razor:

<ChildComponent extra="10" />

ChildComponent. Razor:ChildComponent.razor:

<div extra="5" @attributes="AdditionalAttributes" />

[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object> AdditionalAttributes { get; set; }

El <div> representado en el componente Parent contiene extra="10" cuando se pasa a través del atributo adicional:The rendered <div> in the Parent component contains extra="10" when passed through the additional attribute:

<div extra="10" />

Enlace de datosData binding

El enlace de datos tanto a componentes como a elementos DOM se realiza con el @bind atributo.Data binding to both components and DOM elements is accomplished with the @bind attribute. En el ejemplo siguiente se enlaza una propiedad CurrentValue al valor del cuadro de texto:The following example binds a CurrentValue property to the text box's value:

<input @bind="CurrentValue" />

@code {
    private string CurrentValue { get; set; }
}

Cuando el cuadro de texto pierde el foco, se actualiza el valor de la propiedad.When the text box loses focus, the property's value is updated.

El cuadro de texto se actualiza en la interfaz de usuario solo cuando se representa el componente, no en respuesta a cambiar el valor de la propiedad.The text box is updated in the UI only when the component is rendered, not in response to changing the property's value. Puesto que los componentes se representan por sí solos después de que se ejecute el código del controlador de eventos, las actualizaciones de propiedades normalmente se reflejan en la interfaz de usuario inmediatamente después de desencadenarse un controlador de eventos.Since components render themselves after event handler code executes, property updates are usually reflected in the UI immediately after an event handler is triggered.

El uso de @bind con la propiedad CurrentValue (<input @bind="CurrentValue" />) es esencialmente equivalente a lo siguiente:Using @bind with the CurrentValue property (<input @bind="CurrentValue" />) is essentially equivalent to the following:

<input value="@CurrentValue"
    @onchange="@((ChangeEventArgs __e) => CurrentValue = 
        __e.Value.ToString())" />
        
@code {
    private string CurrentValue { get; set; }
}

Cuando se representa el componente, el value del elemento de entrada procede de la propiedad CurrentValue.When the component is rendered, the value of the input element comes from the CurrentValue property. Cuando el usuario escribe en el cuadro de texto y cambia el foco del elemento, se desencadena el evento onchange y la propiedad CurrentValue se establece en el valor modificado.When the user types in the text box and changes element focus, the onchange event is fired and the CurrentValue property is set to the changed value. En realidad, la generación de código es más compleja porque @bind administra los casos en los que se realizan las conversiones de tipos.In reality, the code generation is more complex because @bind handles cases where type conversions are performed. En principio, @bind asocia el valor actual de una expresión con un atributo value y controla los cambios mediante el controlador registrado.In principle, @bind associates the current value of an expression with a value attribute and handles changes using the registered handler.

Además de controlar los eventos de onchange con sintaxis de @bind, se puede enlazar una propiedad o un campo mediante otros eventos mediante la especificación de un atributo de @bind-value con un parámetro de event (@bind-value:event).In addition to handling onchange events with @bind syntax, a property or field can be bound using other events by specifying an @bind-value attribute with an event parameter (@bind-value:event). En el siguiente ejemplo se enlaza la propiedad CurrentValue del evento oninput:The following example binds the CurrentValue property for the oninput event:

<input @bind-value="CurrentValue" @bind-value:event="oninput" />

@code {
    private string CurrentValue { get; set; }
}

A diferencia de onchange, que se activa cuando el elemento pierde el foco, oninput se desencadena cuando cambia el valor del cuadro de texto.Unlike onchange, which fires when the element loses focus, oninput fires when the value of the text box changes.

Valores no analizablesUnparsable values

Cuando un usuario proporciona un valor que no se pueda analizar a un elemento DataBound, el valor no analizable se revierte automáticamente a su valor anterior cuando se desencadena el evento de enlace.When a user provides an unparsable value to a databound element, the unparsable value is automatically reverted to its previous value when the bind event is triggered.

Considere el siguiente escenario:Consider the following scenario:

  • Un elemento <input> se enlaza a un tipo int con un valor inicial de 123:An <input> element is bound to an int type with an initial value of 123:

    <input @bind="MyProperty" />
    
    @code {
        [Parameter]
        public int MyProperty { get; set; } = 123;
    }
    
  • El usuario actualiza el valor del elemento a 123.45 en la página y cambia el foco del elemento.The user updates the value of the element to 123.45 in the page and changes the element focus.

En el escenario anterior, el valor del elemento se revierte a 123.In the preceding scenario, the element's value is reverted to 123. Cuando el valor 123.45 se rechaza en favor del valor original de 123, el usuario entiende que no se aceptó su valor.When the value 123.45 is rejected in favor of the original value of 123, the user understands that their value wasn't accepted.

De forma predeterminada, el enlace se aplica al evento onchange del elemento (@bind="{PROPERTY OR FIELD}").By default, binding applies to the element's onchange event (@bind="{PROPERTY OR FIELD}"). Use @bind-value="{PROPERTY OR FIELD}" @bind-value:event={EVENT} para establecer un evento diferente.Use @bind-value="{PROPERTY OR FIELD}" @bind-value:event={EVENT} to set a different event. En el caso del evento oninput (@bind-value:event="oninput"), la reversión se produce después de cualquier pulsación de tecla que introduzca un valor no analizable.For the oninput event (@bind-value:event="oninput"), the reversion occurs after any keystroke that introduces an unparsable value. Cuando el destino es el evento oninput con un tipo enlazado a int, se impide que un usuario escriba un carácter ..When targeting the oninput event with an int-bound type, a user is prevented from typing a . character. Se quita inmediatamente un carácter ., por lo que el usuario recibe comentarios inmediatos que solo se permiten números enteros.A . character is immediately removed, so the user receives immediate feedback that only whole numbers are permitted. Hay escenarios en los que la reversión del valor del evento oninput no es ideal, por ejemplo, cuando se debe permitir que el usuario borre un valor <input> que no se puede analizar.There are scenarios where reverting the value on the oninput event isn't ideal, such as when the user should be allowed to clear an unparsable <input> value. Las alternativas incluyen:Alternatives include:

  • No utilice el evento oninput.Don't use the oninput event. Use el evento onchange predeterminado (@bind="{PROPERTY OR FIELD}"), donde no se revierte un valor no válido hasta que el elemento pierde el foco.Use the default onchange event (@bind="{PROPERTY OR FIELD}"), where an invalid value isn't reverted until the element loses focus.
  • Enlazar a un tipo que acepta valores NULL, como int? o string, y proporcionar una lógica personalizada para controlar las entradas no válidas.Bind to a nullable type, such as int? or string, and provide custom logic to handle invalid entries.
  • Use un componente de validación de formulario, como InputNumber o InputDate.Use a form validation component, such as InputNumber or InputDate. Los componentes de validación de formularios tienen compatibilidad integrada para administrar entradas no válidas.Form validation components have built-in support to manage invalid inputs. Componentes de validación de formularios:Form validation components:
    • Permite que el usuario proporcione entradas no válidas y reciba errores de validación en la EditContextasociada.Permit the user to provide invalid input and receive validation errors on the associated EditContext.
    • Mostrar errores de validación en la interfaz de usuario sin interferir con el usuario al escribir datos de WebForm adicionales.Display validation errors in the UI without interfering with the user entering additional webform data.

GlobalizaciónGlobalization

@bind se da formato a los valores para mostrarlos y analizarlos con las reglas de la referencia cultural actual.@bind values are formatted for display and parsed using the current culture's rules.

Se puede tener acceso a la referencia cultural actual desde la propiedad System.Globalization.CultureInfo.CurrentCulture.The current culture can be accessed from the System.Globalization.CultureInfo.CurrentCulture property.

CultureInfo. InvariantCulture se usa para los siguientes tipos de campo (<input type="{TYPE}" />):CultureInfo.InvariantCulture is used for the following field types (<input type="{TYPE}" />):

  • date
  • number

Los tipos de campo anteriores:The preceding field types:

  • Se muestran con sus reglas de formato basadas en explorador adecuadas.Are displayed using their appropriate browser-based formatting rules.
  • No puede contener texto de forma libre.Can't contain free-form text.
  • Proporcionar características de interacción con el usuario en función de la implementación del explorador.Provide user interaction characteristics based on the browser's implementation.

Los siguientes tipos de campo tienen requisitos de formato específicos y no se admiten actualmente en Blazor porque no son compatibles con todos los exploradores principales:The following field types have specific formatting requirements and aren't currently supported by Blazor because they aren't supported by all major browsers:

  • datetime-local
  • month
  • week

@bind admite el parámetro @bind:culture para proporcionar un System.Globalization.CultureInfo para analizar y dar formato a un valor.@bind supports the @bind:culture parameter to provide a System.Globalization.CultureInfo for parsing and formatting a value. No se recomienda especificar una referencia cultural al usar los tipos de campo date y number.Specifying a culture isn't recommended when using the date and number field types. date y number tienen compatibilidad integrada con Blazor que proporciona la referencia cultural necesaria.date and number have built-in Blazor support that provides the required culture.

Para obtener información sobre cómo establecer la referencia cultural del usuario, consulte la sección Localización.For information on how to set the user's culture, see the Localization section.

Cadenas de formatoFormat strings

El enlace de datos funciona con cadenas de formato DateTime mediante @bind:format.Data binding works with DateTime format strings using @bind:format. Otras expresiones de formato, como los formatos de moneda o número, no están disponibles en este momento.Other format expressions, such as currency or number formats, aren't available at this time.

<input @bind="StartDate" @bind:format="yyyy-MM-dd" />

@code {
    [Parameter]
    public DateTime StartDate { get; set; } = new DateTime(2020, 1, 1);
}

En el código anterior, el tipo de campo del elemento de <input> (type) tiene como valor predeterminado text.In the preceding code, the <input> element's field type (type) defaults to text. @bind:format es compatible con el enlace de los siguientes tipos de .NET:@bind:format is supported for binding the following .NET types:

El atributo @bind:format especifica el formato de fecha que se va a aplicar al value del elemento <input>.The @bind:format attribute specifies the date format to apply to the value of the <input> element. El formato también se usa para analizar el valor cuando se produce un evento onchange.The format is also used to parse the value when an onchange event occurs.

No se recomienda especificar un formato para el tipo de campo date porque Blazor tiene compatibilidad integrada para dar formato a las fechas.Specifying a format for the date field type isn't recommended because Blazor has built-in support to format dates. A pesar de la recomendación, use solo el formato de fecha yyyy-MM-dd para que el enlace funcione correctamente si se proporciona un formato con el tipo de campo date:In spite of the recommendation, only use the yyyy-MM-dd date format for binding to work correctly if a format is supplied with the date field type:

<input type="date" @bind="StartDate" @bind:format="yyyy-MM-dd">

Parámetros de componenteComponent parameters

El enlace reconoce los parámetros de componente, donde @bind-{property} puede enlazar un valor de propiedad entre los componentes.Binding recognizes component parameters, where @bind-{property} can bind a property value across components.

El siguiente componente secundario (ChildComponent) tiene un parámetro de componente Year y una devolución de llamada YearChanged:The following child component (ChildComponent) has a Year component parameter and YearChanged callback:

<h2>Child Component</h2>

<p>Year: @Year</p>

@code {
    [Parameter]
    public int Year { get; set; }

    [Parameter]
    public EventCallback<int> YearChanged { get; set; }
}

EventCallback<T> se explica en la sección EventCallback .EventCallback<T> is explained in the EventCallback section.

El siguiente componente primario utiliza ChildComponent y enlaza el parámetro ParentYear desde el elemento primario al parámetro Year en el componente secundario:The following parent component uses ChildComponent and binds the ParentYear parameter from the parent to the Year parameter on the child component:

@page "/ParentComponent"

<h1>Parent Component</h1>

<p>ParentYear: @ParentYear</p>

<ChildComponent @bind-Year="ParentYear" />

<button class="btn btn-primary" @onclick="ChangeTheYear">
    Change Year to 1986
</button>

@code {
    [Parameter]
    public int ParentYear { get; set; } = 1978;

    private void ChangeTheYear()
    {
        ParentYear = 1986;
    }
}

Al cargar el ParentComponent se produce el marcado siguiente:Loading the ParentComponent produces the following markup:

<h1>Parent Component</h1>

<p>ParentYear: 1978</p>

<h2>Child Component</h2>

<p>Year: 1978</p>

Si el valor de la propiedad ParentYear se cambia seleccionando el botón en el ParentComponent, se actualiza la propiedad Year del ChildComponent.If the value of the ParentYear property is changed by selecting the button in the ParentComponent, the Year property of the ChildComponent is updated. El nuevo valor de Year se representa en la interfaz de usuario cuando se representa el ParentComponent:The new value of Year is rendered in the UI when the ParentComponent is rerendered:

<h1>Parent Component</h1>

<p>ParentYear: 1986</p>

<h2>Child Component</h2>

<p>Year: 1986</p>

El parámetro Year es enlazable porque tiene un evento Companion YearChanged que coincide con el tipo del parámetro Year.The Year parameter is bindable because it has a companion YearChanged event that matches the type of the Year parameter.

Por Convención, <ChildComponent @bind-Year="ParentYear" /> es esencialmente equivalente a escribir:By convention, <ChildComponent @bind-Year="ParentYear" /> is essentially equivalent to writing:

<ChildComponent @bind-Year="ParentYear" @bind-Year:event="YearChanged" />

En general, una propiedad se puede enlazar a un controlador de eventos correspondiente mediante @bind-property:event atributo.In general, a property can be bound to a corresponding event handler using @bind-property:event attribute. Por ejemplo, la propiedad MyProp se puede enlazar a MyEventHandler mediante los dos atributos siguientes:For example, the property MyProp can be bound to MyEventHandler using the following two attributes:

<MyComponent @bind-MyProp="MyValue" @bind-MyProp:event="MyEventHandler" />

Control de eventosEvent handling

Los componentes de Razor proporcionan características de control de eventos.Razor components provide event handling features. Para un atributo de elemento HTML denominado on{EVENT} (por ejemplo, onclick y onsubmit) con un valor de tipo delegado, los componentes de Razor tratan el valor del atributo como un controlador de eventos.For an HTML element attribute named on{EVENT} (for example, onclick and onsubmit) with a delegate-typed value, Razor components treats the attribute's value as an event handler. Siempre se da formato al nombre del atributo @on{EVENT}.The attribute's name is always formatted @on{EVENT}.

El código siguiente llama al método UpdateHeading cuando el botón está seleccionado en la interfaz de usuario:The following code calls the UpdateHeading method when the button is selected in the UI:

<button class="btn btn-primary" @onclick="UpdateHeading">
    Update heading
</button>

@code {
    private void UpdateHeading(MouseEventArgs e)
    {
        ...
    }
}

El código siguiente llama al método CheckChanged cuando se cambia la casilla en la interfaz de usuario:The following code calls the CheckChanged method when the check box is changed in the UI:

<input type="checkbox" class="form-check-input" @onchange="CheckChanged" />

@code {
    private void CheckChanged()
    {
        ...
    }
}

Los controladores de eventos también pueden ser asincrónicos y devolver un Task.Event handlers can also be asynchronous and return a Task. No es necesario llamar a StateHasChangedmanualmente.There's no need to manually call StateHasChanged. Las excepciones se registran cuando se producen.Exceptions are logged when they occur.

En el ejemplo siguiente, se llama a UpdateHeading de forma asincrónica cuando se selecciona el botón:In the following example, UpdateHeading is called asynchronously when the button is selected:

<button class="btn btn-primary" @onclick="UpdateHeading">
    Update heading
</button>

@code {
    private async Task UpdateHeading(MouseEventArgs e)
    {
        ...
    }
}

Tipos de argumentos de eventoEvent argument types

En algunos eventos, se permiten los tipos de argumento de evento.For some events, event argument types are permitted. Si no es necesario el acceso a uno de estos tipos de evento, no es necesario en la llamada al método.If access to one of these event types isn't necessary, it isn't required in the method call.

Los EventArgs admitidos se muestran en la tabla siguiente.Supported EventArgs are shown in the following table.

EventEvent ClaseClass Eventos y notas de DOMDOM events and notes
PortapapelesClipboard ClipboardEventArgs oncut, oncopy, onpasteoncut, oncopy, onpaste
ArrastrarDrag DragEventArgs ondrag, ondragstart, ondragenter, ondragleave, ondragover, ondrop, ondragendondrag, ondragstart, ondragenter, ondragleave, ondragover, ondrop, ondragend

DataTransfer y DataTransferItem mantener los datos de los elementos arrastrados.DataTransfer and DataTransferItem hold dragged item data.
Error de :Error ErrorEventArgs onerror
EventEvent EventArgs GeneralGeneral
onactivate, onbeforeactivate, onbeforedeactivate, ondeactivate, onended, onfullscreenchange, onfullscreenerror, onloadeddata, onloadedmetadata, onpointerlockchange, onpointerlockerror, onreadystatechange, onscrollonactivate, onbeforeactivate, onbeforedeactivate, ondeactivate, onended, onfullscreenchange, onfullscreenerror, onloadeddata, onloadedmetadata, onpointerlockchange, onpointerlockerror, onreadystatechange, onscroll

PortapapelesClipboard
onbeforecut, onbeforecopy, onbeforepasteonbeforecut, onbeforecopy, onbeforepaste

EntradaInput
oninvalid, onreset, onselect, onselectionchange, onselectstart, onsubmitoninvalid, onreset, onselect, onselectionchange, onselectstart, onsubmit

MediosMedia
oncanplay, oncanplaythrough, oncuechange, ondurationchange, onemptied, onpause, onplay, onplaying, onratechange, onseeked, onseeking, onstalled, onstop, onsuspend, ontimeupdate, onvolumechange, onwaitingoncanplay, oncanplaythrough, oncuechange, ondurationchange, onemptied, onpause, onplay, onplaying, onratechange, onseeked, onseeking, onstalled, onstop, onsuspend, ontimeupdate, onvolumechange, onwaiting
FocoFocus FocusEventArgs onfocus, onblur, onfocusin, onfocusoutonfocus, onblur, onfocusin, onfocusout

No incluye compatibilidad con relatedTarget.Doesn't include support for relatedTarget.
InputInput ChangeEventArgs onchange, oninputonchange, oninput
TecladoKeyboard KeyboardEventArgs onkeydown, onkeypress, onkeyuponkeydown, onkeypress, onkeyup
MouseMouse MouseEventArgs onclick, oncontextmenu, ondblclick, onmousedown, onmouseup, onmouseover, onmousemove, onmouseoutonclick, oncontextmenu, ondblclick, onmousedown, onmouseup, onmouseover, onmousemove, onmouseout
PunteroMouse pointer PointerEventArgs onpointerdown, onpointerup, onpointercancel, onpointermove, onpointerover, onpointerout, onpointerenter, onpointerleave, ongotpointercapture, onlostpointercaptureonpointerdown, onpointerup, onpointercancel, onpointermove, onpointerover, onpointerout, onpointerenter, onpointerleave, ongotpointercapture, onlostpointercapture
Rueda del ratónMouse wheel WheelEventArgs onwheel, onmousewheelonwheel, onmousewheel
ProgresoProgress ProgressEventArgs onabort, onload, onloadend, onloadstart, onprogress, ontimeoutonabort, onload, onloadend, onloadstart, onprogress, ontimeout
Entrada táctilTouch TouchEventArgs ontouchstart, ontouchend, ontouchmove, ontouchenter, ontouchleave, ontouchcancelontouchstart, ontouchend, ontouchmove, ontouchenter, ontouchleave, ontouchcancel

TouchPoint representa un único punto de contacto en un dispositivo con distinción de toque.TouchPoint represents a single contact point on a touch-sensitive device.

Para obtener información sobre las propiedades y el comportamiento de control de eventos de los eventos de la tabla anterior, vea clases EventArgs en el origen de referencia (rama dotnet/AspNetCore/versión 3.0).For information on the properties and event handling behavior of the events in the preceding table, see EventArgs classes in the reference source (dotnet/AspNetCore release/3.0 branch).

Expresiones lambdaLambda expressions

También se pueden usar expresiones lambda:Lambda expressions can also be used:

<button @onclick="@(e => Console.WriteLine("Hello, world!"))">Say hello</button>

A menudo resulta cómodo cerrar los valores adicionales, como al recorrer en iteración un conjunto de elementos.It's often convenient to close over additional values, such as when iterating over a set of elements. En el ejemplo siguiente se crean tres botones, cada uno de los cuales llama a UpdateHeading pasar un argumento de evento (MouseEventArgs) y su número de botón (buttonNumber) cuando se selecciona en la interfaz de usuario:The following example creates three buttons, each of which calls UpdateHeading passing an event argument (MouseEventArgs) and its button number (buttonNumber) when selected in the UI:

<h2>@message</h2>

@for (var i = 1; i < 4; i++)
{
    var buttonNumber = i;

    <button class="btn btn-primary"
            @onclick="@(e => UpdateHeading(e, buttonNumber))">
        Button #@i
    </button>
}

@code {
    private string message = "Select a button to learn its position.";

    private void UpdateHeading(MouseEventArgs e, int buttonNumber)
    {
        message = $"You selected Button #{buttonNumber} at " +
            $"mouse position: {e.ClientX} X {e.ClientY}.";
    }
}

Nota

No utilice la variable de bucle (i) en un bucle de for directamente en una expresión lambda.Do not use the loop variable (i) in a for loop directly in a lambda expression. De lo contrario, todas las expresiones lambda usan la misma variable, lo que hace que el valor de isea el mismo en todas las expresiones lambda.Otherwise the same variable is used by all lambda expressions causing i's value to be the same in all lambdas. Capture siempre su valor en una variable local (buttonNumber en el ejemplo anterior) y, a continuación, úsela.Always capture its value in a local variable (buttonNumber in the preceding example) and then use it.

EventCallbackEventCallback

Un escenario común con los componentes anidados es el deseo de ejecutar el método de un componente primario cuando se produce un evento de componente secundario—por ejemplo, cuando se produce un evento de onclick en el elemento secundario.A common scenario with nested components is the desire to run a parent component's method when a child component event occurs—for example, when an onclick event occurs in the child. Para exponer eventos entre componentes, use una EventCallback.To expose events across components, use an EventCallback. Un componente primario puede asignar un método de devolución de llamada al EventCallbackde un componente secundario.A parent component can assign a callback method to a child component's EventCallback.

En el ChildComponent de la aplicación de ejemplo (Components/ChildComponent. Razor) se muestra cómo se configura el controlador de onclick de un botón para recibir un EventCallback delegado de la ParentComponentdel ejemplo.The ChildComponent in the sample app (Components/ChildComponent.razor) demonstrates how a button's onclick handler is set up to receive an EventCallback delegate from the sample's ParentComponent. El EventCallback se escribe con MouseEventArgs, que es adecuado para un evento de onclick desde un dispositivo periférico:The EventCallback is typed with MouseEventArgs, which is appropriate for an onclick event from a peripheral device:

<div class="panel panel-default">
    <div class="panel-heading">@Title</div>
    <div class="panel-body">@ChildContent</div>

    <button class="btn btn-primary" @onclick="OnClick">
        Trigger a Parent component method
    </button>
</div>

@code {
    [Parameter]
    public string Title { get; set; }

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    [Parameter]
    public EventCallback<MouseEventArgs> OnClick { get; set; }
}

El ParentComponent establece el EventCallback<T> del elemento secundario en su método ShowMessage.The ParentComponent sets the child's EventCallback<T> to its ShowMessage method.

Pages/ParentComponent. Razor:Pages/ParentComponent.razor:

@page "/ParentComponent"

<h1>Parent-child example</h1>

<ChildComponent Title="Panel Title from Parent"
                OnClick="@ShowMessage">
    Content of the child component is supplied
    by the parent component.
</ChildComponent>

<p><b>@messageText</b></p>

@code {
    private string messageText;

    private void ShowMessage(MouseEventArgs e)
    {
        messageText = $"Blaze a new trail with Blazor! ({e.ScreenX}, {e.ScreenY})";
    }
}

Cuando el botón está seleccionado en el ChildComponent:When the button is selected in the ChildComponent:

  • Se llama al método ShowMessage del ParentComponent.The ParentComponent's ShowMessage method is called. messageText se actualiza y se muestra en el ParentComponent.messageText is updated and displayed in the ParentComponent.
  • No se requiere una llamada a StateHasChanged en el método de la devolución de llamada (ShowMessage).A call to StateHasChanged isn't required in the callback's method (ShowMessage). se llama a StateHasChanged automáticamente para rerepresentar el ParentComponent, del mismo modo que los eventos secundarios desencadenan la rerepresentación de componentes en los controladores de eventos que se ejecutan dentro del elemento secundario.StateHasChanged is called automatically to rerender the ParentComponent, just as child events trigger component rerendering in event handlers that execute within the child.

EventCallback y EventCallback<T> permiten los delegados asincrónicos.EventCallback and EventCallback<T> permit asynchronous delegates. EventCallback<T> tiene un tipo seguro y requiere un tipo de argumento específico.EventCallback<T> is strongly typed and requires a specific argument type. EventCallback tiene un tipo débil y permite cualquier tipo de argumento.EventCallback is weakly typed and allows any argument type.

<p><b>@messageText</b></p>

@{ var message = "Default Text"; }

<ChildComponent 
    OnClick="@(async () => { await Task.Yield(); messageText = "Blaze It!"; })" />

@code {
    private string messageText;
}

Invocar un EventCallback o EventCallback<T> con InvokeAsync y esperar el Task:Invoke an EventCallback or EventCallback<T> with InvokeAsync and await the Task:

await callback.InvokeAsync(arg);

Use EventCallback y EventCallback<T> para el control de eventos y los parámetros de componente de enlace.Use EventCallback and EventCallback<T> for event handling and binding component parameters.

Prefiera el EventCallback<T> fuertemente tipado en EventCallback.Prefer the strongly typed EventCallback<T> over EventCallback. EventCallback<T> proporciona mejores comentarios de errores a los usuarios del componente.EventCallback<T> provides better error feedback to users of the component. Al igual que otros controladores de eventos de la interfaz de usuario, la especificación del parámetro Event es opcional.Similar to other UI event handlers, specifying the event parameter is optional. Use EventCallback cuando no haya ningún valor pasado a la devolución de llamada.Use EventCallback when there's no value passed to the callback.

Impedir acciones predeterminadasPrevent default actions

Use el atributo de directiva de @on{EVENT}:preventDefault para evitar la acción predeterminada de un evento.Use the @on{EVENT}:preventDefault directive attribute to prevent the default action for an event.

Cuando se selecciona una clave en un dispositivo de entrada y el foco del elemento está en un cuadro de texto, un explorador muestra normalmente el carácter de la tecla en el cuadro de texto.When a key is selected on an input device and the element focus is on a text box, a browser normally displays the key's character in the text box. En el ejemplo siguiente, el comportamiento predeterminado se evita especificando el atributo de directiva de @onkeypress:preventDefault.In the following example, the default behavior is prevented by specifying the @onkeypress:preventDefault directive attribute. El contador se incrementa y la clave + no se captura en el valor del elemento <input>:The counter increments, and the + key isn't captured into the <input> element's value:

<input value="@_count" @onkeypress="KeyHandler" @onkeypress:preventDefault />

@code {
    private int _count = 0;

    private void KeyHandler(KeyboardEventArgs e)
    {
        if (e.Key == "+")
        {
            _count++;
        }
    }
}

Especificar el @on{EVENT}:preventDefault atributo sin un valor es equivalente a @on{EVENT}:preventDefault="true".Specifying the @on{EVENT}:preventDefault attribute without a value is equivalent to @on{EVENT}:preventDefault="true".

El valor del atributo también puede ser una expresión.The value of the attribute can also be an expression. En el ejemplo siguiente, _shouldPreventDefault es un campo bool establecido en true o false:In the following example, _shouldPreventDefault is a bool field set to either true or false:

<input @onkeypress:preventDefault="_shouldPreventDefault" />

No es necesario un controlador de eventos para evitar la acción predeterminada.An event handler isn't required to prevent the default action. El controlador de eventos y evitar escenarios de acción predeterminados se pueden usar de forma independiente.The event handler and prevent default action scenarios can be used independently.

Detener propagación de eventosStop event propagation

Use el atributo de directiva de @on{EVENT}:stopPropagation para detener la propagación de eventos.Use the @on{EVENT}:stopPropagation directive attribute to stop event propagation.

En el ejemplo siguiente, al activar la casilla se impide que los eventos de clic de la segunda <div> secundaria se propaguen al <div>primario:In the following example, selecting the check box prevents click events from the second child <div> from propagating to the parent <div>:

<label>
    <input @bind="_stopPropagation" type="checkbox" />
    Stop Propagation
</label>

<div @onclick="OnSelectParentDiv">
    <h3>Parent div</h3>

    <div @onclick="OnSelectChildDiv">
        Child div that doesn't stop propagation when selected.
    </div>

    <div @onclick="OnSelectChildDiv" @onclick:stopPropagation="_stopPropagation">
        Child div that stops propagation when selected.
    </div>
</div>

@code {
    private bool _stopPropagation = false;

    private void OnSelectParentDiv() => 
        Console.WriteLine($"The parent div was selected. {DateTime.Now}");
    private void OnSelectChildDiv() => 
        Console.WriteLine($"A child div was selected. {DateTime.Now}");
}

Enlace encadenadoChained bind

Un escenario común es encadenar un parámetro enlazado a datos a un elemento Page en la salida del componente.A common scenario is chaining a data-bound parameter to a page element in the component's output. Este escenario se denomina enlace encadenado porque se producen varios niveles de enlace simultáneamente.This scenario is called a chained bind because multiple levels of binding occur simultaneously.

No se puede implementar un enlace encadenado con @bind sintaxis en el elemento de la página.A chained bind can't be implemented with @bind syntax in the page's element. El controlador de eventos y el valor se deben especificar por separado.The event handler and value must be specified separately. Sin embargo, un componente primario puede usar la sintaxis de @bind con el parámetro del componente.A parent component, however, can use @bind syntax with the component's parameter.

El siguiente componente de PasswordField (PasswordField. Razor):The following PasswordField component (PasswordField.razor):

  • Establece el valor de un elemento de <input> en una propiedad Password.Sets an <input> element's value to a Password property.
  • Expone los cambios de la propiedad Password a un componente primario con un EventCallback.Exposes changes of the Password property to a parent component with an EventCallback.
Password: 

<input @oninput="OnPasswordChanged" 
       required 
       type="@(showPassword ? "text" : "password")" 
       value="@Password" />

<button class="btn btn-primary" @onclick="ToggleShowPassword">
    Show password
</button>

@code {
    private bool showPassword;

    [Parameter]
    public string Password { get; set; }

    [Parameter]
    public EventCallback<string> PasswordChanged { get; set; }

    private Task OnPasswordChanged(ChangeEventArgs e)
    {
        Password = e.Value.ToString();

        return PasswordChanged.InvokeAsync(Password);
    }

    private void ToggleShowPassword()
    {
        showPassword = !showPassword;
    }
}

El componente de PasswordField se usa en otro componente:The PasswordField component is used in another component:

<PasswordField @bind-Password="password" />

@code {
    private string password;
}

Para realizar comprobaciones o errores de captura en la contraseña en el ejemplo anterior:To perform checks or trap errors on the password in the preceding example:

  • Cree un campo de respaldo para Password (password en el siguiente código de ejemplo).Create a backing field for Password (password in the following example code).
  • Realice las comprobaciones o errores de captura en el establecedor de Password.Perform the checks or trap errors in the Password setter.

En el ejemplo siguiente se proporcionan comentarios inmediatos al usuario si se usa un espacio en el valor de la contraseña:The following example provides immediate feedback to the user if a space is used in the password's value:

Password: 

<input @oninput="OnPasswordChanged" 
       required 
       type="@(showPassword ? "text" : "password")" 
       value="@Password" />

<button class="btn btn-primary" @onclick="ToggleShowPassword">
    Show password
</button>

<span class="text-danger">@validationMessage</span>

@code {
    private bool showPassword;
    private string password;
    private string validationMessage;

    [Parameter]
    public string Password
    {
        get { return password ?? string.Empty; }
        set
        {
            if (password != value)
            {
                if (value.Contains(' '))
                {
                    validationMessage = "Spaces not allowed!";
                }
                else
                {
                    password = value;
                    validationMessage = string.Empty;
                }
            }
        }
    }

    [Parameter]
    public EventCallback<string> PasswordChanged { get; set; }

    private Task OnPasswordChanged(ChangeEventArgs e)
    {
        Password = e.Value.ToString();

        return PasswordChanged.InvokeAsync(Password);
    }

    private void ToggleShowPassword()
    {
        showPassword = !showPassword;
    }
}

Capturar referencias a componentesCapture references to components

Las referencias de componente proporcionan una manera de hacer referencia a una instancia de componente para que pueda emitir comandos a esa instancia, como Show o Reset.Component references provide a way to reference a component instance so that you can issue commands to that instance, such as Show or Reset. Para capturar una referencia de componente:To capture a component reference:

  • Agregue un atributo @ref al componente secundario.Add an @ref attribute to the child component.
  • Defina un campo con el mismo tipo que el componente secundario.Define a field with the same type as the child component.
<MyLoginDialog @ref="loginDialog" ... />

@code {
    private MyLoginDialog loginDialog;

    private void OnSomething()
    {
        loginDialog.Show();
    }
}

Cuando se representa el componente, el campo de loginDialog se rellena con la instancia del componente secundario MyLoginDialog.When the component is rendered, the loginDialog field is populated with the MyLoginDialog child component instance. Después, puede invocar métodos .NET en la instancia del componente.You can then invoke .NET methods on the component instance.

Importante

La variable loginDialog solo se rellena después de que el componente se represente y su salida incluye el elemento MyLoginDialog.The loginDialog variable is only populated after the component is rendered and its output includes the MyLoginDialog element. Hasta ese momento, no hay nada que hacer referencia.Until that point, there's nothing to reference. Para manipular las referencias de componentes una vez finalizada la representación del componente, use los métodos OnAfterRenderAsync o OnAfterRender.To manipulate components references after the component has finished rendering, use the OnAfterRenderAsync or OnAfterRender methods.

Al capturar referencias de componentes, use una sintaxis similar para capturar referencias de elemento, no es una característica de interoperabilidad de JavaScript .While capturing component references use a similar syntax to capturing element references, it isn't a JavaScript interop feature. Las referencias a componentes no se pasan al código JavaScript—solo se usan en código .NET.Component references aren't passed to JavaScript code—they're only used in .NET code.

Nota

No utilice referencias de componentes para mutar el estado de los componentes secundarios.Do not use component references to mutate the state of child components. En su lugar, use parámetros declarativos normales para pasar datos a componentes secundarios.Instead, use normal declarative parameters to pass data to child components. El uso de parámetros declarativos normales da como resultado componentes secundarios que se representarán automáticamente en las horas correctas.Use of normal declarative parameters result in child components that rerender at the correct times automatically.

Invocar métodos de componentes externamente para actualizar el estadoInvoke component methods externally to update state

Blazor usa un SynchronizationContext para aplicar un único subproceso lógico de ejecución. uses a SynchronizationContext to enforce a single logical thread of execution. Los métodos de ciclo de vida de un componente y las devoluciones de llamada de eventos que se producen en Blazor se ejecutan en esta SynchronizationContext.A component's lifecycle methods and any event callbacks that are raised by Blazor are executed on this SynchronizationContext. En caso de que un componente deba actualizarse en función de un evento externo, como un temporizador u otras notificaciones, use el método InvokeAsync, que se enviará al SynchronizationContextde Blazor.In the event a component must be updated based on an external event, such as a timer or other notifications, use the InvokeAsync method, which will dispatch to Blazor's SynchronizationContext.

Por ejemplo, considere un servicio de notificador que puede notificar a cualquier componente de escucha del estado actualizado:For example, consider a notifier service that can notify any listening component of the updated state:

public class NotifierService
{
    // Can be called from anywhere
    public async Task Update(string key, int value)
    {
        if (Notify != null)
        {
            await Notify.Invoke(key, value);
        }
    }

    public event Func<string, int, Task> Notify;
}

Uso de la NotifierService para actualizar un componente:Usage of the NotifierService to update a component:

@page "/"
@inject NotifierService Notifier
@implements IDisposable

<p>Last update: @lastNotification.key = @lastNotification.value</p>

@code {
    private (string key, int value) lastNotification;

    protected override void OnInitialized()
    {
        Notifier.Notify += OnNotify;
    }

    public async Task OnNotify(string key, int value)
    {
        await InvokeAsync(() =>
        {
            lastNotification = (key, value);
            StateHasChanged();
        });
    }

    public void Dispose()
    {
        Notifier.Notify -= OnNotify;
    }
}

En el ejemplo anterior, NotifierService invoca el método de OnNotify del componente fuera del SynchronizationContextde Blazor.In the preceding example, NotifierService invokes the component's OnNotify method outside of Blazor's SynchronizationContext. InvokeAsync se utiliza para cambiar al contexto correcto y poner en cola una representación.InvokeAsync is used to switch to the correct context and queue a render.

Usar @clave para controlar la preservación de elementos y componentesUse @key to control the preservation of elements and components

Cuando se representa una lista de elementos o componentes, y los elementos o componentes cambian posteriormente, el algoritmo de comparación de Blazordebe decidir cuáles de los elementos o componentes anteriores se pueden conservar y cómo deben asignarse los objetos de modelo.When rendering a list of elements or components and the elements or components subsequently change, Blazor's diffing algorithm must decide which of the previous elements or components can be retained and how model objects should map to them. Normalmente, este proceso es automático y se puede omitir, pero hay casos en los que puede que desee controlar el proceso.Normally, this process is automatic and can be ignored, but there are cases where you may want to control the process.

Considere el ejemplo siguiente:Consider the following example:

@foreach (var person in People)
{
    <DetailsEditor Details="person.Details" />
}

@code {
    [Parameter]
    public IEnumerable<Person> People { get; set; }
}

El contenido de la colección de People puede cambiar con entradas insertadas, eliminadas o reordenadas.The contents of the People collection may change with inserted, deleted, or re-ordered entries. Cuando se representa el componente, el componente de <DetailsEditor> puede cambiar para recibir diferentes valores de parámetro de Details.When the component rerenders, the <DetailsEditor> component may change to receive different Details parameter values. Esto puede producir una rerepresentación más compleja de lo esperado.This may cause more complex rerendering than expected. En algunos casos, la rerepresentación puede producir diferencias de comportamiento visibles, como el foco del elemento perdido.In some cases, rerendering can lead to visible behavior differences, such as lost element focus.

El proceso de asignación se puede controlar con el @key atributo de directiva.The mapping process can be controlled with the @key directive attribute. @key hace que el algoritmo de comparación garantice la preservación de elementos o componentes basados en el valor de la clave:@key causes the diffing algorithm to guarantee preservation of elements or components based on the key's value:

@foreach (var person in People)
{
    <DetailsEditor @key="person" Details="person.Details" />
}

@code {
    [Parameter]
    public IEnumerable<Person> People { get; set; }
}

Cuando cambia la colección de People, el algoritmo de comparación mantiene la asociación entre <DetailsEditor> instancias y person instancias:When the People collection changes, the diffing algorithm retains the association between <DetailsEditor> instances and person instances:

  • Si se elimina un Person de la lista de People, solo se quita la instancia de <DetailsEditor> correspondiente de la interfaz de usuario.If a Person is deleted from the People list, only the corresponding <DetailsEditor> instance is removed from the UI. Otras instancias permanecen sin cambios.Other instances are left unchanged.
  • Si se inserta un Person en alguna posición de la lista, se inserta una nueva instancia de <DetailsEditor> en la posición correspondiente.If a Person is inserted at some position in the list, one new <DetailsEditor> instance is inserted at that corresponding position. Otras instancias permanecen sin cambios.Other instances are left unchanged.
  • Si se vuelven a ordenar Person entradas, se conservan y se vuelven a ordenar las instancias de <DetailsEditor> correspondientes en la interfaz de usuario.If Person entries are re-ordered, the corresponding <DetailsEditor> instances are preserved and re-ordered in the UI.

En algunos escenarios, el uso de @key minimiza la complejidad de la rerepresentación y evita posibles problemas con las partes con estado del DOM que cambian, como la posición del foco.In some scenarios, use of @key minimizes the complexity of rerendering and avoids potential issues with stateful parts of the DOM changing, such as focus position.

Importante

Las claves son locales para cada elemento contenedor o componente.Keys are local to each container element or component. Las claves no se comparan globalmente en todo el documento.Keys aren't compared globally across the document.

Cuándo usar la clave de @When to use @key

Normalmente, tiene sentido usar @key cada vez que se representa una lista (por ejemplo, en un bloque de @foreach) y existe un valor adecuado para definir el @key.Typically, it makes sense to use @key whenever a list is rendered (for example, in a @foreach block) and a suitable value exists to define the @key.

También puede usar @key para evitar que Blazor conserven un subárbol de elementos o componentes cuando cambie un objeto:You can also use @key to prevent Blazor from preserving an element or component subtree when an object changes:

<div @key="currentPerson">
    ... content that depends on currentPerson ...
</div>

Si @currentPerson cambia, la Directiva de @key atributo fuerza a Blazor a descartar toda la <div> y sus descendientes y volver a generar el subárbol dentro de la interfaz de usuario con nuevos elementos y componentes.If @currentPerson changes, the @key attribute directive forces Blazor to discard the entire <div> and its descendants and rebuild the subtree within the UI with new elements and components. Esto puede ser útil si necesita garantizar que no se conserva ningún estado de la interfaz de usuario cuando @currentPerson cambios.This can be useful if you need to guarantee that no UI state is preserved when @currentPerson changes.

Cuándo no usar la clave @When not to use @key

Existe un costo de rendimiento al diferenciar con @key.There's a performance cost when diffing with @key. El costo de rendimiento no es grande, pero solo especifica @key si el control del elemento o las reglas de conservación de componentes se benefician de la aplicación.The performance cost isn't large, but only specify @key if controlling the element or component preservation rules benefit the app.

Incluso si no se utiliza @key, Blazor conserva las instancias de componente y elemento secundario lo máximo posible.Even if @key isn't used, Blazor preserves child element and component instances as much as possible. La única ventaja de utilizar @key es el control sobre Cómo se asignan las instancias de modelo a las instancias de componente conservadas, en lugar del algoritmo de diferenciación que selecciona la asignación.The only advantage to using @key is control over how model instances are mapped to the preserved component instances, instead of the diffing algorithm selecting the mapping.

Qué valores se deben usar para la clave de @What values to use for @key

Por lo general, tiene sentido proporcionar uno de los siguientes tipos de valor para @key:Generally, it makes sense to supply one of the following kinds of value for @key:

  • Instancias de objeto de modelo (por ejemplo, una instancia de Person como en el ejemplo anterior).Model object instances (for example, a Person instance as in the earlier example). Esto garantiza la conservación en función de la igualdad de la referencia de objeto.This ensures preservation based on object reference equality.
  • Los identificadores únicos (por ejemplo, los valores de clave principal de tipo int, stringo Guid).Unique identifiers (for example, primary key values of type int, string, or Guid).

Asegúrese de que los valores usados para @key no entren en conflicto.Ensure that values used for @key don't clash. Si se detectan valores en conflicto en el mismo elemento primario, Blazor produce una excepción porque no puede asignar de forma determinista elementos o componentes antiguos a nuevos elementos o componentes.If clashing values are detected within the same parent element, Blazor throws an exception because it can't deterministically map old elements or components to new elements or components. Utilice solo valores distintos, como instancias de objeto o valores de clave principal.Only use distinct values, such as object instances or primary key values.

EnrutamientoRouting

El enrutamiento en Blazor se consigue proporcionando una plantilla de ruta a cada componente accesible en la aplicación.Routing in Blazor is achieved by providing a route template to each accessible component in the app.

Cuando se compila un archivo de Razor con una directiva de @page, a la clase generada se le asigna un RouteAttribute que especifica la plantilla de ruta.When a Razor file with an @page directive is compiled, the generated class is given a RouteAttribute specifying the route template. En tiempo de ejecución, el enrutador busca clases de componentes con un RouteAttribute y representa el componente que tiene una plantilla de ruta que coincide con la dirección URL solicitada.At runtime, the router looks for component classes with a RouteAttribute and renders whichever component has a route template that matches the requested URL.

Se pueden aplicar varias plantillas de ruta a un componente.Multiple route templates can be applied to a component. El siguiente componente responde a las solicitudes de /BlazorRoute y /DifferentBlazorRoute.The following component responds to requests for /BlazorRoute and /DifferentBlazorRoute.

Pages/BlazorRoute. Razor:Pages/BlazorRoute.razor:

@page "/BlazorRoute"
@page "/DifferentBlazorRoute"

<h1>Blazor routing</h1>

Parámetros de rutaRoute parameters

Los componentes pueden recibir parámetros de ruta de la plantilla de ruta proporcionada en la Directiva de @page.Components can receive route parameters from the route template provided in the @page directive. El enrutador usa parámetros de ruta para rellenar los parámetros de componente correspondientes.The router uses route parameters to populate the corresponding component parameters.

Pages/RouteParameter. Razor:Pages/RouteParameter.razor:

@page "/RouteParameter"
@page "/RouteParameter/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }

    protected override void OnInitialized()
    {
        Text = Text ?? "fantastic";
    }
}

Los parámetros opcionales no se admiten, por lo que se aplican dos directivas de @page en el ejemplo anterior.Optional parameters aren't supported, so two @page directives are applied in the example above. El primero permite la navegación al componente sin un parámetro.The first permits navigation to the component without a parameter. La segunda Directiva de @page toma el parámetro {text} Route y asigna el valor a la propiedad Text.The second @page directive takes the {text} route parameter and assigns the value to the Text property.

La sintaxis de los parámetros catch-all (*/**), que capturan la ruta de acceso en varios límites de carpeta, no se admite en los componentes de Razor ( . Razor).Catch-all parameter syntax (*/**), which captures the path across multiple folder boundaries, is not supported in Razor components (.razor).

Compatibilidad de clases parcialesPartial class support

Los componentes de Razor se generan como clases parciales.Razor components are generated as partial classes. Los componentes de Razor se crean mediante cualquiera de los métodos siguientes:Razor components are authored using either of the following approaches:

  • C#el código se define en un bloque @code con marcado HTML y código Razor en un único archivo.C# code is defined in an @code block with HTML markup and Razor code in a single file. Blazor plantillas definen sus componentes de Razor mediante este enfoque. templates define their Razor components using this approach.
  • C#el código se coloca en un archivo de código subyacente definido como una clase parcial.C# code is placed in a code-behind file defined as a partial class.

En el ejemplo siguiente se muestra el componente de Counter predeterminado con un bloque de @code en una aplicación generada a partir de una plantilla de Blazor.The following example shows the default Counter component with an @code block in an app generated from a Blazor template. El marcado HTML, el código Razor C# y el código se encuentran en el mismo archivo:HTML markup, Razor code, and C# code are in the same file:

Counter. Razor:Counter.razor:

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    int currentCount = 0;

    void IncrementCount()
    {
        currentCount++;
    }
}

El componente de Counter también se puede crear con un archivo de código subyacente con una clase parcial:The Counter component can also be created using a code-behind file with a partial class:

Counter. Razor:Counter.razor:

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

Counter.Razor.CS:Counter.razor.cs:

namespace BlazorApp.Pages
{
    public partial class Counter
    {
        int currentCount = 0;

        void IncrementCount()
        {
            currentCount++;
        }
    }
}

Especificar una clase base de componenteSpecify a component base class

La Directiva @inherits se puede utilizar para especificar una clase base para un componente.The @inherits directive can be used to specify a base class for a component.

La aplicación de ejemplo muestra cómo un componente puede heredar una clase base, BlazorRocksBase, para proporcionar las propiedades y los métodos del componente.The sample app shows how a component can inherit a base class, BlazorRocksBase, to provide the component's properties and methods.

Pages/BlazorRocks. Razor:Pages/BlazorRocks.razor:

@page "/BlazorRocks"
@inherits BlazorRocksBase

<h1>@BlazorRocksText</h1>

BlazorRocksBase.cs:BlazorRocksBase.cs:

using Microsoft.AspNetCore.Components;

namespace BlazorSample
{
    public class BlazorRocksBase : ComponentBase
    {
        public string BlazorRocksText { get; set; } = 
            "Blazor rocks the browser!";
    }
}

La clase base debe derivar de ComponentBase.The base class should derive from ComponentBase.

Importar componentesImport components

El espacio de nombres de un componente creado con Razor se basa en (en orden de prioridad):The namespace of a component authored with Razor is based on (in priority order):

  • @namespace designación en el marcado de archivos Razor ( . Razor) (@namespace BlazorSample.MyNamespace).@namespace designation in Razor file (.razor) markup (@namespace BlazorSample.MyNamespace).
  • RootNamespace del proyecto en el archivo de proyecto (<RootNamespace>BlazorSample</RootNamespace>).The project's RootNamespace in the project file (<RootNamespace>BlazorSample</RootNamespace>).
  • Nombre del proyecto, tomado del nombre de archivo del archivo de proyecto ( . csproj) y la ruta de acceso de la raíz del proyecto al componente.The project name, taken from the project file's file name (.csproj), and the path from the project root to the component. Por ejemplo, el marco de trabajo resuelve {root Project}/pages/index.Razor (BlazorSample. csproj) en el espacio de nombres BlazorSample.Pages.For example, the framework resolves {PROJECT ROOT}/Pages/Index.razor (BlazorSample.csproj) to the namespace BlazorSample.Pages. Los componentes C# siguen las reglas de enlace de nombres.Components follow C# name binding rules. En el caso del componente Index en este ejemplo, los componentes del ámbito son todos los componentes:For the Index component in this example, the components in scope are all of the components:
    • En la misma carpeta, páginas.In the same folder, Pages.
    • Los componentes de la raíz del proyecto que no especifican explícitamente un espacio de nombres diferente.The components in the project's root that don't explicitly specify a different namespace.

Los componentes definidos en un espacio de nombres diferente se incluyen en el ámbito mediante la Directiva de @using de Razor.Components defined in a different namespace are brought into scope using Razor's @using directive.

Si existe otro componente, NavMenu.razor, en la carpeta BlazorSample/Shared/ , el componente se puede utilizar en Index.razor con la siguiente instrucción @using:If another component, NavMenu.razor, exists in the BlazorSample/Shared/ folder, the component can be used in Index.razor with the following @using statement:

@using BlazorSample.Shared

This is the Index page.

<NavMenu></NavMenu>

También se puede hacer referencia a los componentes mediante sus nombres completos, que no requieren la directiva @using :Components can also be referenced using their fully qualified names, which doesn't require the @using directive:

This is the Index page.

<BlazorSample.Shared.NavMenu></BlazorSample.Shared.NavMenu>

Nota

No se admite la calificación global::.The global:: qualification isn't supported.

No se admite la importación de componentes con instrucciones de using con alias (por ejemplo, @using Foo = Bar).Importing components with aliased using statements (for example, @using Foo = Bar) isn't supported.

No se admiten nombres parcialmente completos.Partially qualified names aren't supported. Por ejemplo, no se admite agregar @using BlazorSample y hacer referencia a NavMenu.razor con <Shared.NavMenu></Shared.NavMenu>.For example, adding @using BlazorSample and referencing NavMenu.razor with <Shared.NavMenu></Shared.NavMenu> isn't supported.

Atributos de elementos HTML condicionalesConditional HTML element attributes

Los atributos de elemento HTML se representan condicionalmente según el valor de .NET.HTML element attributes are conditionally rendered based on the .NET value. Si el valor es false o null, el atributo no se representa.If the value is false or null, the attribute isn't rendered. Si el valor es true, el atributo se representa minimizado.If the value is true, the attribute is rendered minimized.

En el ejemplo siguiente, IsCompleted determina si checked se representa en el marcado del elemento:In the following example, IsCompleted determines if checked is rendered in the element's markup:

<input type="checkbox" checked="@IsCompleted" />

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}

Si IsCompleted se true, la casilla se representa como:If IsCompleted is true, the check box is rendered as:

<input type="checkbox" checked />

Si IsCompleted se false, la casilla se representa como:If IsCompleted is false, the check box is rendered as:

<input type="checkbox" />

Para obtener más información, vea Referencia de sintaxis de Razor para ASP.NET Core.For more information, see Referencia de sintaxis de Razor para ASP.NET Core.

Advertencia

Algunos atributos HTML, como Aria-pressed, no funcionan correctamente cuando el tipo .net es un bool.Some HTML attributes, such as aria-pressed, don't function properly when the .NET type is a bool. En esos casos, use un tipo de string en lugar de un bool.In those cases, use a string type instead of a bool.

HTML sin formatoRaw HTML

Normalmente, las cadenas se representan mediante nodos de texto DOM, lo que significa que cualquier marcado que pueda contener se omite y se trata como texto literal.Strings are normally rendered using DOM text nodes, which means that any markup they may contain is ignored and treated as literal text. Para representar HTML sin formato, ajuste el contenido HTML en un valor de MarkupString.To render raw HTML, wrap the HTML content in a MarkupString value. El valor se analiza como HTML o SVG y se inserta en el DOM.The value is parsed as HTML or SVG and inserted into the DOM.

Advertencia

La representación de HTML sin formato construido a partir de un origen que no es de confianza es un riesgo para la seguridad y debe evitarse.Rendering raw HTML constructed from any untrusted source is a security risk and should be avoided!

En el ejemplo siguiente se muestra el uso del tipo de MarkupString para agregar un bloque de contenido HTML estático a la salida representada de un componente:The following example shows using the MarkupString type to add a block of static HTML content to the rendered output of a component:

@((MarkupString)myMarkup)

@code {
    private string myMarkup = 
        "<p class='markup'>This is a <em>markup string</em>.</p>";
}

Componentes con plantillaTemplated components

Los componentes con plantilla son componentes que aceptan una o varias plantillas de interfaz de usuario como parámetros, que se pueden usar como parte de la lógica de representación del componente.Templated components are components that accept one or more UI templates as parameters, which can then be used as part of the component's rendering logic. Los componentes con plantilla permiten crear componentes de nivel superior que son más reutilizables que los componentes normales.Templated components allow you to author higher-level components that are more reusable than regular components. Algunos ejemplos son:A couple of examples include:

  • Componente de tabla que permite a un usuario especificar plantillas para el encabezado, las filas y el pie de página de la tabla.A table component that allows a user to specify templates for the table's header, rows, and footer.
  • Componente de lista que permite a un usuario especificar una plantilla para representar elementos en una lista.A list component that allows a user to specify a template for rendering items in a list.

Parámetros de plantillaTemplate parameters

Un componente con plantilla se define especificando uno o más parámetros de componente de tipo RenderFragment o RenderFragment<T>.A templated component is defined by specifying one or more component parameters of type RenderFragment or RenderFragment<T>. Un fragmento de representación representa un segmento de interfaz de usuario que se va a representar.A render fragment represents a segment of UI to render. RenderFragment<T> toma un parámetro de tipo que se puede especificar cuando se invoca el fragmento de representación.RenderFragment<T> takes a type parameter that can be specified when the render fragment is invoked.

TableTemplate componente:TableTemplate component:

@typeparam TItem

<table class="table">
    <thead>
        <tr>@TableHeader</tr>
    </thead>
    <tbody>
        @foreach (var item in Items)
        {
            <tr>@RowTemplate(item)</tr>
        }
    </tbody>
    <tfoot>
        <tr>@TableFooter</tr>
    </tfoot>
</table>

@code {
    [Parameter]
    public RenderFragment TableHeader { get; set; }

    [Parameter]
    public RenderFragment<TItem> RowTemplate { get; set; }

    [Parameter]
    public RenderFragment TableFooter { get; set; }

    [Parameter]
    public IReadOnlyList<TItem> Items { get; set; }
}

Cuando se usa un componente con plantilla, los parámetros de plantilla se pueden especificar utilizando los elementos secundarios que coinciden con los nombres de los parámetros (TableHeader y RowTemplate en el ejemplo siguiente):When using a templated component, the template parameters can be specified using child elements that match the names of the parameters (TableHeader and RowTemplate in the following example):

<TableTemplate Items="pets">
    <TableHeader>
        <th>ID</th>
        <th>Name</th>
    </TableHeader>
    <RowTemplate>
        <td>@context.PetId</td>
        <td>@context.Name</td>
    </RowTemplate>
</TableTemplate>

Parámetros de contexto de plantillaTemplate context parameters

Los argumentos de los componentes de tipo RenderFragment<T> pasados como elementos tienen un parámetro implícito denominado context (por ejemplo, en el ejemplo de código anterior, @context.PetId), pero puede cambiar el nombre del parámetro mediante el atributo Context en el elemento secundario.Component arguments of type RenderFragment<T> passed as elements have an implicit parameter named context (for example from the preceding code sample, @context.PetId), but you can change the parameter name using the Context attribute on the child element. En el ejemplo siguiente, el atributo Context del elemento RowTemplate especifica el parámetro pet:In the following example, the RowTemplate element's Context attribute specifies the pet parameter:

<TableTemplate Items="pets">
    <TableHeader>
        <th>ID</th>
        <th>Name</th>
    </TableHeader>
    <RowTemplate Context="pet">
        <td>@pet.PetId</td>
        <td>@pet.Name</td>
    </RowTemplate>
</TableTemplate>

También puede especificar el atributo de Context en el elemento de componente.Alternatively, you can specify the Context attribute on the component element. El atributo de Context especificado se aplica a todos los parámetros de plantilla especificados.The specified Context attribute applies to all specified template parameters. Esto puede ser útil si desea especificar el nombre del parámetro de contenido para el contenido secundario implícito (sin ningún elemento secundario de ajuste).This can be useful when you want to specify the content parameter name for implicit child content (without any wrapping child element). En el ejemplo siguiente, el atributo Context aparece en el elemento TableTemplate y se aplica a todos los parámetros de plantilla:In the following example, the Context attribute appears on the TableTemplate element and applies to all template parameters:

<TableTemplate Items="pets" Context="pet">
    <TableHeader>
        <th>ID</th>
        <th>Name</th>
    </TableHeader>
    <RowTemplate>
        <td>@pet.PetId</td>
        <td>@pet.Name</td>
    </RowTemplate>
</TableTemplate>

Componentes de tipo genéricoGeneric-typed components

Los componentes con plantilla suelen tener tipos genéricos.Templated components are often generically typed. Por ejemplo, se puede usar un componente de ListViewTemplate genérico para representar valores de IEnumerable<T>.For example, a generic ListViewTemplate component can be used to render IEnumerable<T> values. Para definir un componente genérico, utilice la directiva @typeparam para especificar parámetros de tipo:To define a generic component, use the @typeparam directive to specify type parameters:

@typeparam TItem

<ul>
    @foreach (var item in Items)
    {
        @ItemTemplate(item)
    }
</ul>

@code {
    [Parameter]
    public RenderFragment<TItem> ItemTemplate { get; set; }

    [Parameter]
    public IReadOnlyList<TItem> Items { get; set; }
}

Cuando se usan componentes de tipo genérico, el parámetro de tipo se infiere si es posible:When using generic-typed components, the type parameter is inferred if possible:

<ListViewTemplate Items="pets">
    <ItemTemplate Context="pet">
        <li>@pet.Name</li>
    </ItemTemplate>
</ListViewTemplate>

De lo contrario, el parámetro de tipo se debe especificar explícitamente mediante un atributo que coincida con el nombre del parámetro de tipo.Otherwise, the type parameter must be explicitly specified using an attribute that matches the name of the type parameter. En el ejemplo siguiente, TItem="Pet" especifica el tipo:In the following example, TItem="Pet" specifies the type:

<ListViewTemplate Items="pets" TItem="Pet">
    <ItemTemplate Context="pet">
        <li>@pet.Name</li>
    </ItemTemplate>
</ListViewTemplate>

Valores y parámetros en cascadaCascading values and parameters

En algunos escenarios, no es conveniente fluir los datos de un componente antecesor a un componente descendiente mediante parámetros de componente, especialmente cuando hay varios niveles de componentes.In some scenarios, it's inconvenient to flow data from an ancestor component to a descendent component using component parameters, especially when there are several component layers. Los valores y parámetros en cascada solucionan este problema proporcionando un método cómodo para que un componente antecesor proporcione un valor a todos sus componentes descendientes.Cascading values and parameters solve this problem by providing a convenient way for an ancestor component to provide a value to all of its descendent components. Los valores y parámetros en cascada también proporcionan un enfoque para que los componentes se coordinen.Cascading values and parameters also provide an approach for components to coordinate.

Ejemplo de temaTheme example

En el ejemplo siguiente de la aplicación de ejemplo, la clase ThemeInfo especifica la información del tema que va a fluir hacia abajo en la jerarquía de componentes para que todos los botones de una parte determinada de la aplicación compartan el mismo estilo.In the following example from the sample app, the ThemeInfo class specifies the theme information to flow down the component hierarchy so that all of the buttons within a given part of the app share the same style.

UIThemeClasses/ThemeInfo.cs:UIThemeClasses/ThemeInfo.cs:

public class ThemeInfo
{
    public string ButtonClass { get; set; }
}

Un componente antecesor puede proporcionar un valor en cascada mediante el componente de valor en cascada.An ancestor component can provide a cascading value using the Cascading Value component. El componente CascadingValue encapsula un subárbol de la jerarquía de componentes y proporciona un valor único a todos los componentes de ese subárbol.The CascadingValue component wraps a subtree of the component hierarchy and supplies a single value to all components within that subtree.

Por ejemplo, la aplicación de ejemplo especifica información de tema (ThemeInfo) en uno de los diseños de la aplicación como parámetro en cascada para todos los componentes que componen el cuerpo del diseño de la propiedad @Body.For example, the sample app specifies theme information (ThemeInfo) in one of the app's layouts as a cascading parameter for all components that make up the layout body of the @Body property. a ButtonClass se le asigna un valor de btn-success en el componente de diseño.ButtonClass is assigned a value of btn-success in the layout component. Cualquier componente descendiente puede consumir esta propiedad mediante el ThemeInfo objeto en cascada.Any descendent component can consume this property through the ThemeInfo cascading object.

CascadingValuesParametersLayout componente:CascadingValuesParametersLayout component:

@inherits LayoutComponentBase
@using BlazorSample.UIThemeClasses

<div class="container-fluid">
    <div class="row">
        <div class="col-sm-3">
            <NavMenu />
        </div>
        <div class="col-sm-9">
            <CascadingValue Value="theme">
                <div class="content px-4">
                    @Body
                </div>
            </CascadingValue>
        </div>
    </div>
</div>

@code {
    private ThemeInfo theme = new ThemeInfo { ButtonClass = "btn-success" };
}

Para usar valores en cascada, los componentes declaran parámetros en cascada mediante el atributo [CascadingParameter].To make use of cascading values, components declare cascading parameters using the [CascadingParameter] attribute. Los valores en cascada se enlazan a los parámetros en cascada por tipo.Cascading values are bound to cascading parameters by type.

En la aplicación de ejemplo, el componente CascadingValuesParametersTheme enlaza el valor en cascada ThemeInfo a un parámetro en cascada.In the sample app, the CascadingValuesParametersTheme component binds the ThemeInfo cascading value to a cascading parameter. El parámetro se usa para establecer la clase CSS para uno de los botones mostrados por el componente.The parameter is used to set the CSS class for one of the buttons displayed by the component.

CascadingValuesParametersTheme componente:CascadingValuesParametersTheme component:

@page "/cascadingvaluesparameterstheme"
@layout CascadingValuesParametersLayout
@using BlazorSample.UIThemeClasses

<h1>Cascading Values & Parameters</h1>

<p>Current count: @currentCount</p>

<p>
    <button class="btn" @onclick="IncrementCount">
        Increment Counter (Unthemed)
    </button>
</p>

<p>
    <button class="btn @ThemeInfo.ButtonClass" @onclick="IncrementCount">
        Increment Counter (Themed)
    </button>
</p>

@code {
    private int currentCount = 0;

    [CascadingParameter]
    protected ThemeInfo ThemeInfo { get; set; }

    private void IncrementCount()
    {
        currentCount++;
    }
}

Para poner en cascada varios valores del mismo tipo en el mismo subárbol, proporcione una cadena de Name única para cada componente CascadingValue y su CascadingParametercorrespondiente.To cascade multiple values of the same type within the same subtree, provide a unique Name string to each CascadingValue component and its corresponding CascadingParameter. En el ejemplo siguiente, dos componentes de CascadingValue en cascada son instancias diferentes de MyCascadingType por nombre:In the following example, two CascadingValue components cascade different instances of MyCascadingType by name:

<CascadingValue Value=@ParentCascadeParameter1 Name="CascadeParam1">
    <CascadingValue Value=@ParentCascadeParameter2 Name="CascadeParam2">
        ...
    </CascadingValue>
</CascadingValue>

@code {
    private MyCascadingType ParentCascadeParameter1;

    [Parameter]
    public MyCascadingType ParentCascadeParameter2 { get; set; }

    ...
}

En un componente descendiente, los parámetros en cascada reciben sus valores de los valores en cascada correspondientes del componente antecesor por nombre:In a descendant component, the cascaded parameters receive their values from the corresponding cascaded values in the ancestor component by name:

...

@code {
    [CascadingParameter(Name = "CascadeParam1")]
    protected MyCascadingType ChildCascadeParameter1 { get; set; }
    
    [CascadingParameter(Name = "CascadeParam2")]
    protected MyCascadingType ChildCascadeParameter2 { get; set; }
}

Ejemplo de TabSetTabSet example

Los parámetros en cascada también permiten que los componentes colaboren en la jerarquía de componentes.Cascading parameters also enable components to collaborate across the component hierarchy. Por ejemplo, considere el siguiente ejemplo de TabSet en la aplicación de ejemplo.For example, consider the following TabSet example in the sample app.

La aplicación de ejemplo tiene una interfaz ITab que las pestañas implementan:The sample app has an ITab interface that tabs implement:

using Microsoft.AspNetCore.Components;

namespace BlazorSample.UIInterfaces
{
    public interface ITab
    {
        RenderFragment ChildContent { get; }
    }
}

El componente CascadingValuesParametersTabSet usa el componente TabSet, que contiene varios componentes Tab:The CascadingValuesParametersTabSet component uses the TabSet component, which contains several Tab components:

<TabSet>
    <Tab Title="First tab">
        <h4>Greetings from the first tab!</h4>

        <label>
            <input type="checkbox" @bind="showThirdTab" />
            Toggle third tab
        </label>
    </Tab>
    <Tab Title="Second tab">
        <h4>The second tab says Hello World!</h4>
    </Tab>

    @if (showThirdTab)
    {
        <Tab Title="Third tab">
            <h4>Welcome to the disappearing third tab!</h4>
            <p>Toggle this tab from the first tab.</p>
        </Tab>
    }
</TabSet>

Los componentes de Tab secundarios no se pasan explícitamente como parámetros a la TabSet.The child Tab components aren't explicitly passed as parameters to the TabSet. En su lugar, los componentes secundarios Tab forman parte del contenido secundario del TabSet.Instead, the child Tab components are part of the child content of the TabSet. Sin embargo, el TabSet todavía necesita saber sobre cada componente Tab para que pueda representar los encabezados y la pestaña activa. Para habilitar esta coordinación sin necesidad de código adicional, el componente de TabSet puede proporcionarse como un valor en cascada que los componentes de Tab descendientes recogen.However, the TabSet still needs to know about each Tab component so that it can render the headers and the active tab. To enable this coordination without requiring additional code, the TabSet component can provide itself as a cascading value that is then picked up by the descendent Tab components.

TabSet componente:TabSet component:

@using BlazorSample.UIInterfaces

<!-- Display the tab headers -->
<CascadingValue Value=this>
    <ul class="nav nav-tabs">
        @ChildContent
    </ul>
</CascadingValue>

<!-- Display body for only the active tab -->
<div class="nav-tabs-body p-4">
    @ActiveTab?.ChildContent
</div>

@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    public ITab ActiveTab { get; private set; }

    public void AddTab(ITab tab)
    {
        if (ActiveTab == null)
        {
            SetActivateTab(tab);
        }
    }

    public void RemoveTab(ITab tab)
    {
        if (ActiveTab == tab)
        {
            SetActivateTab(null);
        }
    }

    public void SetActivateTab(ITab tab)
    {
        if (ActiveTab != tab)
        {
            ActiveTab = tab;
            StateHasChanged();
        }
    }
}

Los componentes de Tab descendientes capturan el TabSet contenedor como un parámetro en cascada, por lo que los componentes de Tab se agregan a los TabSet y coordenadas en los que la pestaña está activa.The descendent Tab components capture the containing TabSet as a cascading parameter, so the Tab components add themselves to the TabSet and coordinate on which tab is active.

Tab componente:Tab component:

@using BlazorSample.UIInterfaces
@implements ITab

<li>
    <a @onclick="Activate" class="nav-link @TitleCssClass" role="button">
        @Title
    </a>
</li>

@code {
    [CascadingParameter]
    public TabSet ContainerTabSet { get; set; }

    [Parameter]
    public string Title { get; set; }

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    private string TitleCssClass => ContainerTabSet.ActiveTab == this ? "active" : null;

    protected override void OnInitialized()
    {
        ContainerTabSet.AddTab(this);
    }

    private void Activate()
    {
        ContainerTabSet.SetActivateTab(this);
    }
}

Plantillas de RazorRazor templates

Los fragmentos de representación se pueden definir mediante la sintaxis de plantilla de Razor.Render fragments can be defined using Razor template syntax. Las plantillas de Razor son una manera de definir un fragmento de la interfaz de usuario y suponer el siguiente formato:Razor templates are a way to define a UI snippet and assume the following format:

@<{HTML tag}>...</{HTML tag}>

En el ejemplo siguiente se muestra cómo especificar los valores de RenderFragment y RenderFragment<T> y las plantillas de representación directamente en un componente.The following example illustrates how to specify RenderFragment and RenderFragment<T> values and render templates directly in a component. Los fragmentos de representación también se pueden pasar como argumentos a componentes con plantilla.Render fragments can also be passed as arguments to templated components.

@timeTemplate

@petTemplate(new Pet { Name = "Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = 
        (pet) => @<p>Your pet's name is @pet.Name.</p>;

    private class Pet
    {
        public string Name { get; set; }
    }
}

Salida representada del código anterior:Rendered output of the preceding code:

<p>The time is 10/04/2018 01:26:52.</p>

<p>Your pet's name is Rex.</p>

Lógica de RenderTreeBuilder manualManual RenderTreeBuilder logic

Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder proporciona métodos para manipular componentes y elementos, incluida la creación manual de componentes C# en el código.Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder provides methods for manipulating components and elements, including building components manually in C# code.

Nota

El uso de RenderTreeBuilder para crear componentes es un escenario avanzado.Use of RenderTreeBuilder to create components is an advanced scenario. Un componente con formato incorrecto (por ejemplo, una etiqueta de marcado sin cerrar) puede dar lugar a un comportamiento indefinido.A malformed component (for example, an unclosed markup tag) can result in undefined behavior.

Tenga en cuenta el siguiente componente de PetDetails, que se puede integrar manualmente en otro componente:Consider the following PetDetails component, which can be manually built into another component:

<h2>Pet Details Component</h2>

<p>@PetDetailsQuote</p>

@code
{
    [Parameter]
    public string PetDetailsQuote { get; set; }
}

En el ejemplo siguiente, el bucle del método CreateComponent genera tres componentes PetDetails.In the following example, the loop in the CreateComponent method generates three PetDetails components. Al llamar a métodos RenderTreeBuilder para crear los componentes (OpenComponent y AddAttribute), los números de secuencia son números de línea de código fuente.When calling RenderTreeBuilder methods to create the components (OpenComponent and AddAttribute), sequence numbers are source code line numbers. El algoritmo de diferencia de Blazor se basa en los números de secuencia correspondientes a líneas de código distintas, no a invocaciones de llamada distintas.The Blazor difference algorithm relies on the sequence numbers corresponding to distinct lines of code, not distinct call invocations. Al crear un componente con métodos de RenderTreeBuilder, codifique los argumentos para los números de secuencia.When creating a component with RenderTreeBuilder methods, hardcode the arguments for sequence numbers. El uso de un cálculo o un contador para generar el número de secuencia puede dar lugar a un rendimiento deficiente.Using a calculation or counter to generate the sequence number can lead to poor performance. Para obtener más información, vea la sección números de secuencia relacionados con números de línea de código y no orden de ejecución .For more information, see the Sequence numbers relate to code line numbers and not execution order section.

BuiltContent componente:BuiltContent component:

@page "/BuiltContent"

<h1>Build a component</h1>

@CustomRender

<button type="button" @onclick="RenderComponent">
    Create three Pet Details components
</button>

@code {
    private RenderFragment CustomRender { get; set; }
    
    private RenderFragment CreateComponent() => builder =>
    {
        for (var i = 0; i < 3; i++) 
        {
            builder.OpenComponent(0, typeof(PetDetails));
            builder.AddAttribute(1, "PetDetailsQuote", "Someone's best friend!");
            builder.CloseComponent();
        }
    };    
    
    private void RenderComponent()
    {
        CustomRender = CreateComponent();
    }
}

Advertencia

Los tipos de Microsoft.AspNetCore.Components.RenderTree permiten el procesamiento de los resultados de las operaciones de representación.The types in Microsoft.AspNetCore.Components.RenderTree allow processing of the results of rendering operations. Estos son los detalles internos de la implementación del marco de Blazor.These are internal details of the Blazor framework implementation. Estos tipos se deben considerar inestables y estar sujetos a cambios en futuras versiones.These types should be considered unstable and subject to change in future releases.

Los números de secuencia se relacionan con los números de línea de código y no el orden de ejecuciónSequence numbers relate to code line numbers and not execution order

los archivos de .razor de Blazor siempre se compilan.Blazor .razor files are always compiled. Esta es potencialmente una gran ventaja para .razor porque el paso de compilación se puede usar para insertar información que mejoran el rendimiento de las aplicaciones en tiempo de ejecución.This is potentially a great advantage for .razor because the compile step can be used to inject information that improve app performance at runtime.

Un ejemplo clave de estas mejoras implican los números de secuencia.A key example of these improvements involve sequence numbers. Los números de secuencia indican al tiempo de ejecución los resultados de los que proceden las líneas de código distintas y ordenadas.Sequence numbers indicate to the runtime which outputs came from which distinct and ordered lines of code. El motor en tiempo de ejecución utiliza esta información para generar diferencias de árbol eficientes en el tiempo lineal, que es mucho más rápido de lo que suele ser posible para un algoritmo de comparación de árboles generales.The runtime uses this information to generate efficient tree diffs in linear time, which is far faster than is normally possible for a general tree diff algorithm.

Considere el siguiente archivo de componente de Razor ( . Razor):Consider the following Razor component (.razor) file:

@if (someFlag)
{
    <text>First</text>
}

Second

El código anterior se compila en algo similar a lo siguiente:The preceding code compiles to something like the following:

if (someFlag)
{
    builder.AddContent(0, "First");
}

builder.AddContent(1, "Second");

Cuando el código se ejecuta por primera vez, si se true``someFlag, el generador recibe:When the code executes for the first time, if someFlag is true, the builder receives:

SecuenciaSequence Tipo deType DatosData
00 Nodo de textoText node FirstFirst
11 Nodo de textoText node SecondSecond

Imagine que someFlag se falsey que el marcado se representará de nuevo.Imagine that someFlag becomes false, and the markup is rendered again. Esta vez, el generador recibe:This time, the builder receives:

SecuenciaSequence Tipo deType DatosData
11 Nodo de textoText node SecondSecond

Cuando el tiempo de ejecución realiza una comparación, ve que se quitó el elemento en la secuencia 0, por lo que genera el siguiente script de edicióntrivial:When the runtime performs a diff, it sees that the item at sequence 0 was removed, so it generates the following trivial edit script:

  • Quitar el primer nodo de texto.Remove the first text node.

Qué sucede si se generan números de secuencia mediante programaciónWhat goes wrong if you generate sequence numbers programmatically

Imagine que escribió la siguiente lógica del generador de árboles de representación:Imagine instead that you wrote the following render tree builder logic:

var seq = 0;

if (someFlag)
{
    builder.AddContent(seq++, "First");
}

builder.AddContent(seq++, "Second");

Ahora, el primer resultado es:Now, the first output is:

SecuenciaSequence Tipo deType DatosData
00 Nodo de textoText node FirstFirst
11 Nodo de textoText node SecondSecond

Este resultado es idéntico al caso anterior, por lo que no existe ningún problema negativo.This outcome is identical to the prior case, so no negative issues exist. someFlag se false en la segunda representación y el resultado es:someFlag is false on the second rendering, and the output is:

SecuenciaSequence Tipo deType DatosData
00 Nodo de textoText node SecondSecond

Esta vez, el algoritmo de comparación ve que se han producido dos cambios y el algoritmo genera el siguiente script de edición:This time, the diff algorithm sees that two changes have occurred, and the algorithm generates the following edit script:

  • Cambie el valor del primer nodo de texto a Second.Change the value of the first text node to Second.
  • Quite el segundo nodo de texto.Remove the second text node.

La generación de los números de secuencia ha perdido toda la información útil sobre el lugar en el que las bifurcaciones de if/else y los bucles estaban presentes en el código original.Generating the sequence numbers has lost all the useful information about where the if/else branches and loops were present in the original code. Esto da como resultado una diferencia dos veces mayor que antes.This results in a diff twice as long as before.

Este es un ejemplo trivial.This is a trivial example. En casos más realistas con estructuras complejas y profundamente anidadas, y especialmente con bucles, el costo de rendimiento es más grave.In more realistic cases with complex and deeply nested structures, and especially with loops, the performance cost is more severe. En lugar de identificar inmediatamente qué bloques o ramas de bucle se han insertado o quitado, el algoritmo de comparación tiene que recurse profundamente en los árboles de representación y, normalmente, compilar scripts de edición mucho más largos porque está informada sobre cómo las estructuras antiguas y nuevas se relacionan entre sí.Instead of immediately identifying which loop blocks or branches have been inserted or removed, the diff algorithm has to recurse deeply into the render trees and usually build far longer edit scripts because it is misinformed about how the old and new structures relate to each other.

Instrucciones y conclusionesGuidance and conclusions

  • El rendimiento de la aplicación se ve afectado si los números de secuencia se generan dinámicamente.App performance suffers if sequence numbers are generated dynamically.
  • El marco no puede crear sus propios números de secuencia automáticamente en tiempo de ejecución porque la información necesaria no existe a menos que se Capture en tiempo de compilación.The framework can't create its own sequence numbers automatically at runtime because the necessary information doesn't exist unless it's captured at compile time.
  • No escriba grandes bloques de lógica de RenderTreeBuilder implementada de forma manual.Don't write long blocks of manually-implemented RenderTreeBuilder logic. Prefiere .razor archivos y permitir que el compilador trate los números de secuencia.Prefer .razor files and allow the compiler to deal with the sequence numbers. Si no puede evitar la lógica de RenderTreeBuilder manual, divida bloques largos de código en fragmentos más pequeños encapsulados en OpenRegion/CloseRegion llamadas.If you're unable to avoid manual RenderTreeBuilder logic, split long blocks of code into smaller pieces wrapped in OpenRegion/CloseRegion calls. Cada región tiene su propio espacio independiente de números de secuencia, por lo que puede reiniciar desde cero (o cualquier otro número arbitrario) dentro de cada región.Each region has its own separate space of sequence numbers, so you can restart from zero (or any other arbitrary number) inside each region.
  • Si los números de secuencia están codificados, el algoritmo de comparación solo requiere que los números de secuencia aumenten en valor.If sequence numbers are hardcoded, the diff algorithm only requires that sequence numbers increase in value. El valor inicial y los huecos son irrelevantes.The initial value and gaps are irrelevant. Una opción legítima es usar el número de línea de código como el número de secuencia, o comenzar a partir de cero y aumentar por unos o cientos (o cualquier intervalo preferido).One legitimate option is to use the code line number as the sequence number, or start from zero and increase by ones or hundreds (or any preferred interval).
  • Blazor usa los números de secuencia, mientras que otros marcos de interfaz de usuario de comparación de árboles no los utilizan. uses sequence numbers, while other tree-diffing UI frameworks don't use them. La diferenciación es mucho más rápida cuando se utilizan números de secuencia y Blazor tiene la ventaja de un paso de compilación que trata los números de secuencia automáticamente para desarrolladores que crean archivos . Razor .Diffing is far faster when sequence numbers are used, and Blazor has the advantage of a compile step that deals with sequence numbers automatically for developers authoring .razor files.

LocalizaciónLocalization

las aplicaciones de Blazor Server se localizan mediante middleware de localización.Blazor Server apps are localized using Localization Middleware. El middleware selecciona la referencia cultural adecuada para los usuarios que solicitan recursos de la aplicación.The middleware selects the appropriate culture for users requesting resources from the app.

La referencia cultural se puede establecer utilizando uno de los métodos siguientes:The culture can be set using one of the following approaches:

Para obtener más información y ejemplos, vea Globalización y localización en ASP.NET Core.For more information and examples, see Globalización y localización en ASP.NET Core.

Configurar el enlazador para la internacionalización (Blazor webassembly)Configure the linker for internationalization (Blazor WebAssembly)

De forma predeterminada, la configuración del enlazador de Blazor para aplicaciones WebAssembly de Blazor quita información de internacionalización, excepto para las configuraciones regionales solicitadas de forma explícita.By default, Blazor's linker configuration for Blazor WebAssembly apps strips out internationalization information except for locales explicitly requested. Para obtener más información e instrucciones sobre cómo controlar el comportamiento del enlazador, vea Configuración del enlazador de ASP.NET Core Blazor.For more information and guidance on controlling the linker's behavior, see Configuración del enlazador de ASP.NET Core Blazor.

CookiesCookies

Una cookie de referencia cultural de localización puede conservar la referencia cultural del usuario.A localization culture cookie can persist the user's culture. La cookie se crea mediante el método OnGet de la página host de la aplicación (pages/host. cshtml. CS).The cookie is created by the OnGet method of the app's host page (Pages/Host.cshtml.cs). El middleware de localización lee la cookie en solicitudes posteriores para establecer la referencia cultural del usuario.The Localization Middleware reads the cookie on subsequent requests to set the user's culture.

El uso de una cookie garantiza que la conexión de WebSocket puede propagar correctamente la referencia cultural.Use of a cookie ensures that the WebSocket connection can correctly propagate the culture. Si los esquemas de localización se basan en la ruta de acceso URL o la cadena de consulta, es posible que el esquema no funcione con WebSockets, por lo que no se puede conservar la referencia cultural.If localization schemes are based on the URL path or query string, the scheme might not be able to work with WebSockets, thus fail to persist the culture. Por lo tanto, el uso de una cookie de referencia cultural de localización es el enfoque recomendado.Therefore, use of a localization culture cookie is the recommended approach.

Cualquier técnica se puede usar para asignar una referencia cultural si la referencia cultural se conserva en una cookie de localización.Any technique can be used to assign a culture if the culture is persisted in a localization cookie. Si la aplicación ya tiene un esquema de localización establecido para ASP.NET Core del lado servidor, siga usando la infraestructura de localización existente de la aplicación y establezca la cookie de la cultura de localización en el esquema de la aplicación.If the app already has an established localization scheme for server-side ASP.NET Core, continue to use the app's existing localization infrastructure and set the localization culture cookie within the app's scheme.

En el ejemplo siguiente se muestra cómo establecer la referencia cultural actual en una cookie que puede ser leída por el middleware de localización.The following example shows how to set the current culture in a cookie that can be read by the Localization Middleware. Cree un archivo pages/host. cshtml. CS con el siguiente contenido en la aplicación de Blazor Server:Create a Pages/Host.cshtml.cs file with the following contents in the Blazor Server app:

public class HostModel : PageModel
{
    public void OnGet()
    {
        HttpContext.Response.Cookies.Append(
            CookieRequestCultureProvider.DefaultCookieName,
            CookieRequestCultureProvider.MakeCookieValue(
                new RequestCulture(
                    CultureInfo.CurrentCulture,
                    CultureInfo.CurrentUICulture)));
    }
}

La localización se controla en la aplicación:Localization is handled in the app:

  1. El explorador envía una solicitud HTTP inicial a la aplicación.The browser sends an initial HTTP request to the app.
  2. El middleware de localización asigna la referencia cultural.The culture is assigned by the Localization Middleware.
  3. El método OnGet en _Host. cshtml. CS conserva la referencia cultural en una cookie como parte de la respuesta.The OnGet method in _Host.cshtml.cs persists the culture in a cookie as part of the response.
  4. El explorador abre una conexión WebSocket para crear una sesión interactiva de Blazor Server.The browser opens a WebSocket connection to create an interactive Blazor Server session.
  5. El middleware de localización lee la cookie y asigna la referencia cultural.The Localization Middleware reads the cookie and assigns the culture.
  6. La sesión del servidor de Blazor comienza con la referencia cultural correcta.The Blazor Server session begins with the correct culture.

Proporcionar la interfaz de usuario para elegir la referencia culturalProvide UI to choose the culture

Para proporcionar una interfaz de usuario que permita a los usuarios seleccionar una referencia cultural, se recomienda un enfoque basado en redirección .To provide UI to allow a user to select a culture, a redirect-based approach is recommended. El proceso es similar a lo que ocurre en una aplicación web cuando un usuario intenta acceder a un recurso seguro—se redirige al usuario a una página de inicio de sesión y, a continuación, se redirige de nuevo al recurso original.The process is similar to what happens in a web app when a user attempts to access a secure resource—the user is redirected to a sign-in page and then redirected back to the original resource.

La aplicación conserva la referencia cultural seleccionada del usuario a través de una redirección a un controlador.The app persists the user's selected culture via a redirect to a controller. El controlador establece la referencia cultural seleccionada del usuario en una cookie y redirige al usuario de nuevo al URI original.The controller sets the user's selected culture into a cookie and redirects the user back to the original URI.

Establezca un punto de conexión HTTP en el servidor para establecer la referencia cultural seleccionada del usuario en una cookie y volver a realizar la redirección al URI original:Establish an HTTP endpoint on the server to set the user's selected culture in a cookie and perform the redirect back to the original URI:

[Route("[controller]/[action]")]
public class CultureController : Controller
{
    public IActionResult SetCulture(string culture, string redirectUri)
    {
        if (culture != null)
        {
            HttpContext.Response.Cookies.Append(
                CookieRequestCultureProvider.DefaultCookieName,
                CookieRequestCultureProvider.MakeCookieValue(
                    new RequestCulture(culture)));
        }

        return LocalRedirect(redirectUri);
    }
}

Advertencia

Use el resultado de la acción LocalRedirect para evitar ataques de redireccionamiento abierto.Use the LocalRedirect action result to prevent open redirect attacks. Para obtener más información, vea Evitar ataques de redireccionamiento abierto en ASP.NET Core.For more information, see Evitar ataques de redireccionamiento abierto en ASP.NET Core.

El siguiente componente muestra un ejemplo de cómo realizar la redirección inicial cuando el usuario selecciona una referencia cultural:The following component shows an example of how to perform the initial redirection when the user selects a culture:

@inject NavigationManager NavigationManager

<h3>Select your language</h3>

<select @onchange="OnSelected">
    <option>Select...</option>
    <option value="en-US">English</option>
    <option value="fr-FR">Français</option>
</select>

@code {
    private double textNumber;

    private void OnSelected(ChangeEventArgs e)
    {
        var culture = (string)e.Value;
        var uri = new Uri(NavigationManager.Uri())
            .GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped);
        var query = $"?culture={Uri.EscapeDataString(culture)}&" +
            $"redirectUri={Uri.EscapeDataString(uri)}";

        NavigationManager.NavigateTo("/Culture/SetCulture" + query, forceLoad: true);
    }
}

Uso de escenarios de localización de .NET en Blazor aplicacionesUse .NET localization scenarios in Blazor apps

Dentro de Blazor aplicaciones, están disponibles los siguientes escenarios de localización y globalización de .NET:Inside Blazor apps, the following .NET localization and globalization scenarios are available:

  • . Sistema de recursos de la red.NET's resources system
  • Formato de fecha y número específico de la referencia culturalCulture-specific number and date formatting

la funcionalidad de @bind de Blazorrealiza la globalización en función de la referencia cultural actual del usuario.Blazor's @bind functionality performs globalization based on the user's current culture. Para obtener más información, vea la sección enlace de datos .For more information, see the Data binding section.

Actualmente se admite un conjunto limitado de escenarios de localización de ASP.NET Core:A limited set of ASP.NET Core's localization scenarios are currently supported:

  • IStringLocalizer<> se admite en las aplicaciones de Blazor.IStringLocalizer<> is supported in Blazor apps.
  • la localización de IHtmlLocalizer<>, IViewLocalizer<>y las anotaciones de datos es ASP.NET Core escenarios MVC y no se admiten en Blazor aplicaciones.IHtmlLocalizer<>, IViewLocalizer<>, and Data Annotations localization are ASP.NET Core MVC scenarios and not supported in Blazor apps.

Para obtener más información, vea Globalización y localización en ASP.NET Core.For more information, see Globalización y localización en ASP.NET Core.

Imágenes de gráficos vectoriales escalables (SVG)Scalable Vector Graphics (SVG) images

Dado que Blazor representa imágenes HTML, compatibles con el explorador, incluidas las imágenes SVG (Scalable Vector Graphics) ( . svg), se admiten a través de la etiqueta <img>:Since Blazor renders HTML, browser-supported images, including Scalable Vector Graphics (SVG) images (.svg), are supported via the <img> tag:

<img alt="Example image" src="some-image.svg" />

Del mismo modo, las imágenes SVG se admiten en las reglas CSS de un archivo de hoja de estilos ( . CSS):Similarly, SVG images are supported in the CSS rules of a stylesheet file (.css):

.my-element {
    background-image: url("some-image.svg");
}

Sin embargo, el marcado SVG en línea no se admite en todos los escenarios.However, inline SVG markup isn't supported in all scenarios. Si coloca una etiqueta de <svg> directamente en un archivo de componente ( . Razor), se admite la representación de imágenes básica, pero aún no se admiten muchos escenarios avanzados.If you place an <svg> tag directly into a component file (.razor), basic image rendering is supported but many advanced scenarios aren't yet supported. Por ejemplo, <use> etiquetas no se respetan actualmente y @bind no se pueden usar con algunas etiquetas SVG.For example, <use> tags aren't currently respected, and @bind can't be used with some SVG tags. Esperamos abordar estas limitaciones en una versión futura.We expect to address these limitations in a future release.

Recursos adicionalesAdditional resources