In a blazor C# app, I have a page called TopicVideo.razor with the following code:
<div id="video-input">
<input #bind="YouTubeVideoUrl" type="text" placeholder="Video URL" style="max-height: 30px;">
<button class="summarizeBtn" #onclick="InitiateYouTubeDownload" tabindex="5">OK</button>
</div>
</div>
#code {
private async Task InitiateYouTubeDownload(){
// process
}
I wanted to create a new component called OptionOne.razor, that replaces the div with id "video-input" in the page TopicVideo.razor. So I created this:
<div id="video-input">
<input #bind="YouTubeVideoUrl" type="text" placeholder="Video URL">
<button #onclick="InitiateYouTubeDownload" tabindex="5">OK</button>
</div>
#code {
[Parameter] public string? YouTubeVideoUrl { get; set; }
[Parameter] public EventCallback<MouseEventArgs> InitiateYouTubeDownload { get; set; }
}
and in TopicVideo, I replaced the div with id "video-input" with the following:
<OptionOne YouTubeVideoUrl="YouTubeVideoUrl" InitiateYouTubeDownload="InitiateYouTubeDownload"></OptionOne>
Issue is: It seems the OptionOne component is not passing YouTubeVideoUrl correctly. When I use the VS Code Debugger, hovering over YouTubeVideoUrl in the OptionOne page shows the correct URL, but hovering over it in the TopicVideo.razor page shows "null". How do I pass YouTubeVideoUrl from the child component OptionOne to the parent component TopicVideo?
Since you already have an event registered, you can just use that to pass the URL of the video back to the parent. You could make the following changes:
OptionOne.razor:
<div id="video-input">
<input #bind="this.YouTubeVideoUrl" type="text" placeholder="Video URL">
<button #onclick="this.StartYouTubeDownload" tabindex="5">OK</button>
</div>
#code {
[Parameter] public string? YouTubeVideoUrl { get; set; }
[Parameter] public EventCallback<string> InitiateYouTubeDownload { get; set; }
private async Task StartYouTubeDownload()
{
if (this.InitiateYouTubeDownload.HasDelegate)
{
await this.InitiateYouTubeDownload.InvokeAsync(this.YouTubeVideoUrl);
}
}
}
TopicVideo.razor:
<OptionOne YouTubeVideoUrl="#this.YouTubeVideoUrl" InitiateYouTubeDownload="this.InitiateYouTubeDownload"></OptionOne>
#code {
private string YouTubeVideoUrl;
private void InitiateYouTubeDownload(string url)
{
this.YouTubeVideoUrl = url;
}
}
Related
I created this component. file name: InputTextComponent.razor
<div class="form-group">
<label for="Name">Name</label>
<InputText #bind-Value="#Value"/>
</div>
#code {
[Parameter]
public string Value{ get; set; }
}
in the page index.razor
#page "/"
<EditForm model="#data" OnValidSubmit="()=>OnClickBtn()">
<DataAnnotationsValidator></DataAnnotationsValidator>
<ValidationSummary></ValidationSummary>
<div class="modal-body">
<InputTextComponent Value="data.Name"/>
</div>
<div class="modal-footer justify-content-between">
<button type="button" >Cancel</button>
<button type="submit" class="btn btn-primary">OK</button>
</div>
</EditForm>
#code{
public class Item
{
public string Name { get; set; } = string.Empty;
}
private Item data { get; set; } = new Item(){ Name="John"};
private void OnClickBtn()
{
string k = "";
}
}
When I run, it show :
then i edit the name field. and press "ok" button.
How to pass #bind-value into component. (the same InputText on blazor component).
Thanks all!!
To achieve this you can manually implement the Change event in your child component InputTextComponent. The parameter is called Value, so you would need to implement an EventCallback with the name ValueChanged.
Your InputTextComponent would need to look like this:
<div class="form-group">
<label for="Name">Name</label>
<InputText #oninput="updateParent" #bind-Value="#Value"/>
</div>
#code {
[Parameter]
public string Value { get; set; }
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
async void updateParent(ChangeEventArgs e)
{
ValueChanged.InvokeAsync(e.Value.ToString());
}
}
And then you can use #bind-Value in the parent component like <InputTextComponent #bind-Value="data.Name"/>
Here is a blazor repl showing the full thing.
Here's another way to do it.
I've added a bit of functionality to show you how to build out a classic Bootstrap control.
#inherits InputText
<div class="form-group">
<label class="form-label">#this.Label</label>
#inputControl
<div class="form-text">#this.Commentary</div>
</div>
#code {
[Parameter] public string Label { get; set; }
[Parameter] public string Commentary { get; set; }
// Gets the underlying Renderfragment built by InputText
private RenderFragment inputControl => (builder) => base.BuildRenderTree(builder);
}
And a demo page.
#page "/"
<PageTitle>Index</PageTitle>
<h1>Test Page</h1>
<EditForm Model=this.model>
<MyInputText class="form-control" Label="Value" Commentary="Enter a value" #bind-Value=model.Value />
</EditForm>
<div class="alert alert-info mt-3">
Value : #model.Value
</div>
#code {
private ModelData model = new();
public class ModelData {
public string? Value { get; set; }
}
}
As the other answer showed binding to oninput you can implement that like this. The above component would then inherit from BlazrInputText.
public class BlazrInputText : InputText
{
[Parameter] public bool BindOnInput { get; set; } = true;
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, "input");
builder.AddMultipleAttributes(1, AdditionalAttributes);
if (!string.IsNullOrWhiteSpace(this.CssClass))
builder.AddAttribute(2, "class", CssClass);
builder.AddAttribute(3, "value", BindConverter.FormatValue(CurrentValueAsString));
if (BindOnInput)
builder.AddAttribute(4, "oninput", EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
else
builder.AddAttribute(5, "onchange", EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
builder.AddElementReferenceCapture(6, __inputReference => Element = __inputReference);
builder.CloseElement();
}
}
I want to build some reusable input components using Blazor.
Here is my code in the child component:
<div style="width: 100%">
<div class="create-approval-flow-drop-down">
<Label Display="Display.Flex">#ChildLabel</Label>
<Autocomplete TItem="GraphUser"
TValue="string"
Data="#users"
TextField="#(( item ) => item.DisplayName)"
ValueField="#(( item ) => item.DisplayName)"
Placeholder="Search..."
#bind-SelectedValue="#selectedSearchValue"
#bind-SelectedText="#selectedAutoCompleteText">
</Autocomplete>
</div>
</div>
#code {
[Parameter] public string ChildLabel { get; set; }
[Parameter] public string ChildValue { get; set; }
[Parameter] public string selectedSearchValue { get; set; }
[Parameter] public EventCallback<string> ChildLabelChanged { get; set; }
[Parameter] public EventCallback<string> ChildValueChanged { get; set; }
[Parameter] public EventCallback<string> selectedSearchValueChanged { get; set; }
[Inject] public IGraphService GraphService { get; set; }
public IEnumerable<GraphUser> users = new List<GraphUser>();
protected override async Task OnInitializedAsync()
{
users = await GraphService.GetUsers();
await base.OnInitializedAsync();
}
private Task OnChildValueChanged(ChangeEventArgs e)
{
return ChildValueChanged.InvokeAsync(selectedAutoCompleteText);
}
string selectedAutoCompleteText { get; set; }
}
And here is my parent component:
#page "/Test"
#using WireDesk.Web.SubComponents;
#using Blazored.FluentValidation;
#using WireDesk.Models
<WireDesk.Web.SubComponents.SelectAutocomplete.SingleSelectAutoComplete
ChildLabel="Location Manager"
ChildValue="">
</WireDesk.Web.SubComponents.SelectAutocomplete.SingleSelectAutoComplete>
#ChildLabel;
#ChildValue;
#code
{
private string ChildLabel { get; set; }
private string ChildValue { get; set; }
public ApprovalFlowForm approvalFlowForm = new ApprovalFlowForm();
}
The child will display the label and create a single select autocomplete text box. The parent will contain a form with a variety of fields, many of which will be a single select autocomplete text box.
I don't want to have to duplicate the code in the child over and over in the parent, but I cannot determine how to pass the string that the user has selected in the child component.
=========================================================
This is almost working but not completely, I think I didn't explain two things clearly. The component will be used for input on a create/update page, and I need to use it more than once. The parent component should call the child just to do the work of displaying/updating the value and returning that to the parent, and then the parent will bind that to the Form.Field. Seems to me that the power of components would be that they could be reused, not just in different parents, but multiple times in the same parent (I am sure this is possible, but sure I don't know how to do it).
My code for the child is as so:
<div style="width: 100%">
<div class="create-approval-flow-drop-down">
<Label Display="Display.Flex">#ChildLabel</Label>
<Autocomplete TItem="GraphUser"
TValue="string"
Data="#users"
TextField="#(( item ) => item.DisplayName)"
ValueField="#(( item ) => item.DisplayName)"
Placeholder="Search..."
SelectedValue="#ChildValue"
SelectedValueChanged="OnChildValueChanged">
</Autocomplete>
</div>
</div>
#code {
[Parameter] public string ChildLabel { get; set; }
[Parameter] public string ChildValue { get; set; }
[Parameter] public string FieldValue { get; set; }
[Parameter] public EventCallback<string> ChildValueChanged { get; set; }
[Inject] public IGraphService GraphService { get; set; }
public IEnumerable<GraphUser> users = new List<GraphUser>();
protected override async Task OnInitializedAsync()
{
users = await GraphService.GetUsers();
await base.OnInitializedAsync();
}
private async Task OnChildValueChanged(string selectedValue)
{
ChildValue = selectedValue;
await ChildValueChanged.InvokeAsync(ChildValue);
}
}
And the parent is
#page "/Test"
#using WireDesk.Web.SubComponents;
#using Blazored.FluentValidation;
#using WireDesk.Models
<WireDesk.Web.SubComponents.SelectAutocomplete.SingleSelectAutoComplete
ChildLabel="First Manager"
#bind-ChildValue="#childValue">
#*#bind-SelectedValue="#ApprovalFlowForm.FirstManager"*#
</WireDesk.Web.SubComponents.SelectAutocomplete.SingleSelectAutoComplete>
<WireDesk.Web.SubComponents.SelectAutocomplete.SingleSelectAutoComplete
ChildLabel="Second Manager"
#bind-ChildValue="#childValue">
#*bind-SelectedValue="#ApprovalFlowForm.SecondManager"*#
</WireDesk.Web.SubComponents.SelectAutocomplete.SingleSelectAutoComplete>
#code {
private string childValue;
}
The commented out line...
#bind-SelectedValue="#ApprovalFlowForm.SecondManager"#
..I know will probably not work, but indicates the general idea I think of what I want to accomplish.
Thank you both Joe and Dimitris very very much. I have been working on this for the last 8 hours, and I feel I am pretty close now. I appreciate your assistance.
Bryan
Still struggling with this.
My parent component is below. The "old" way of displaying an input select field is listed, as is my call to the "new" way, which is a child component.
#using Microsoft.IdentityModel.Tokens
#using System.Collections.Immutable
#using System.Linq
//Old
<div class="create-approval-flow-drop-down">
<Label Display="Display.Flex"> Controller </Label>
<Select TValue="string" #bind-SelectedValue="#ApprovalFlowForm.Controller">
#{foreach (var item in GraphUserOptions)
{<SelectItem Value="#item.Value">#item.Text</SelectItem>}
</Select>
</div>
//New
<div class="create-approval-flow-drop-down">
<SingleSelectAutoComplete
ChildLabel="Controller"
DataToSearch=users
#bind-SelectedValue="#ApprovalFlowForm.Controller">
</SingleSelectAutoComplete>
</div>
The child component below works perfectly except for one thing: when the parent edits a form with a value already in the data, it fails to display! So if "Ben Kew" is stored as the Controller in the Approval Form, opening the form in edit mode will not show that. Just a blank field. The user can pick from the list and changes will be saved, but the next time they open up the form they will see blank again.
If you can help me solve this, I would be so very happy and would be a big step in my project.
Thank you in advance for assistance!
Bryan
#using Blazorise.Components
#using System.Linq
<div style="width: 100%">
<div class="create-approval-flow-drop-down">
<Label Display="Display.Flex">#ChildLabel</Label>
<Autocomplete
TItem="GraphUser"
TValue="string"
Data=DataToSearch
TextField="#(( item ) => item.DisplayName)"
ValueField="#(( item ) => item.DisplayName)"
#bind-SelectedText="selectedAutoCompleteText"
SelectedValueChanged="#OnSelectedValueChanged">
Placeholder="Search...">
</Autocomplete>
</div>
</div>
#code {
[Parameter] public string ChildLabel { get; set; }
[Parameter] public IEnumerable<GraphUser> DataToSearch { get; set; }
[Parameter] public string SelectedValue { get; set; }
[Parameter] public EventCallback<string> SelectedValueChanged { get; set; }
[Parameter] public string? Param { get; set; }
public string selectedValue { get; set; }
public string selectedAutoCompleteText { get; set; }
public override async Task SetParametersAsync(ParameterView parameters)
{
}
private async Task OnSelectedValueChanged(string selectedValue)
{
//SelectedValue = selectedValue;
//await SelectedValueChanged.InvokeAsync(SelectedValue);
}
}
You can do it using EventCallback (like #Joe suggested).
<div style="width: 100%">
<div class="create-approval-flow-drop-down">
<Label Display="Display.Flex">#ChildLabel</Label>
<Autocomplete TItem="GraphUser"
TValue="string"
Data="#DataToSearch"
TextField="#(( item ) => item.DisplayName)"
ValueField="#(( item ) => item.DisplayName)"
Placeholder="Search..."
SelectedValue="#SelectedValue"
SelectedValueChanged="#OnSelectedValueChanged">
</Autocomplete>
</div>
</div>
#code {
[Parameter] public string ChildLabel { get; set; }
[Parameter] public IEnumerable<GraphUser> DataToSearch { get; set; }
[Parameter] public string SelectedValue { get; set; }
[Parameter] public EventCallback<string> SelectedValueChanged { get; set; }
private async Task OnSelectedValueChanged(string selectedValue)
{
SelectedValue = selectedValue;
await SelectedValueChanged.InvokeAsync(SelectedValue);
}
}
Usage:
<SingleSelectAutoComplete
ChildLabel="Controller"
DataToSearch="#users"
#bind-SelectedValue="#ApprovalFlowForm.Controller">
</SingleSelectAutoComplete>
Use an EventCallBack in the child and assign it to a function in the parent. Do something like this. In the parent component:
<ChildComponent TextFieldUpdated="HandleChange" />
And the following code:
private void HandleChange(string value){
Do stuff...
}
And in the child component:
[Parameter] public EventCallBack<string> TextFieldUpdated {get;set;}
Then all you have to do is this when you want to send the value to the parent component:
TextFieldUpdated.InvokeAsync("String Value");
HandleChange() in the parent component will fire with the value from the child component.
In my shared folder, I have 4 razor files.
TopNavBar.razor
Sidebar.razor
MainLayout.razor
Footer.razor
Inside MainLayout.razor I have this code
#inherits LayoutComponentBase
<Sidebar/>
<div class="d-flex flex-column" id="content-wrapper">
<div id="content">
<TopNavBar/>
<div class="container-fluid">
#Body
</div>
</div>
<Footer/>
</div>
<a class="border rounded d-inline scroll-to-top" href="#page-top"><i class="fas fa-angle-up"></i></a>
Inside my Sidebar.razor I have this toggle button and the c# code
<button #onclick=toggleButton class="btn rounded-circle border-0" id="sidebarToggle" type="button"></button>
#code {
private string toggled = "";
private void toggleButton() {
if (toggled == "")
toggled = "toggled";
else
toggled = "";
}
}
In some cases, I also need to change the class of <div></div> element in TopNavBar.razor when the toggle button is click.
The question is, How to change the Html tags inside one.razor from two.razor? or how to access variables from razor to razor?
You can achieve this function by passing data with Component parameters between components.
Component parameters pass data to components and are defined using public C# properties on the component class with the [Parameter] attribute.
You can refer to the sample code:
public class PanelBody
{
public string? Text { get; set; }
public string? Style { get; set; }
}
<div class="card w-25" style="margin-bottom:15px">
<div class="card-header font-weight-bold">#Title</div>
<div class="card-body" style="font-style:#Body.Style">
#Body.Text
</div>
</div>
#code {
[Parameter]
public string Title { get; set; } = "Set By Child";
[Parameter]
public PanelBody Body { get; set; } =
new()
{
Text = "Set by child.",
Style = "normal"
};
}
For more, you can check: https://learn.microsoft.com/en-us/aspnet/core/blazor/components/data-binding?view=aspnetcore-6.0#bind-across-more-than-two-components
https://learn.microsoft.com/en-us/aspnet/core/blazor/components/?view=aspnetcore-6.0#component-parameters .
I have a simple page index.razor with a button:
<a class="btn btn-login" #onclick="RedirectPage" >Log in</a>
<div
#bind-Visible="#InvalidLogin"
BodyText="Error">
</div>
#code{
InvalidLogin {get; set;} = false;
}
Where the function RedirectPage checks if values are valid. If they are not, I want a popup giving information:
private void RedirectPage
{
this.InvalidLogin = true;
}
This function is in the index.razor.cs and has been added with #using in the correct namespace.
How can I create it so that a popup shows up whenever the button is clicked?
You can create a simple popup (or modal dialog) component. Below, I wrote a sample popup razor component using Bootstrap 5 toast component.
Popup.razor file
#{
var showClass = IsVisible ? "d-block" : "d-none";
}
<div class="toast-container p-3 #showClass" data-bs-autohide="true" data-bs-delay="5000">
<div class="toast show" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<strong class="me-auto">#HeaderText</strong>
<button type="button" class="btn-close" aria-label="Close" #onclick="Close"></button>
</div>
<div class="toast-body">
#BodyText
</div>
</div>
</div>
#code {
[Parameter]
public bool IsVisible { get; set; }
[Parameter]
public EventCallback<bool> IsVisibleChanged { get; set; }
[Parameter]
public string? HeaderText { get; set; }
[Parameter]
public string? BodyText { get; set; }
public void Show(string bodyText, string headerText = "")
{
HeaderText = headerText;
BodyText = bodyText;
IsVisible = true;
StateHasChanged();
}
private void Close()
{
HeaderText = string.Empty;
BodyText = string.Empty;
IsVisible = false;
StateHasChanged();
}
}
Using the Popup razor component in your code:
<a class="btn btn-login" #onclick="RedirectPage" >Log in</a>
<Popup #ref="popupRef" />
#code{
private Popup popupRef;
private void RedirectPage()
{
// Shows the popup at the center of the screen
popupRef.Show("Popup body text");
}
}
How to create a dialog without a dependency on a third party library.
I had to use a minimal amount of js as the new HTML5 <dialog... element can only be opened in dialog mode with it .showModal() not by manipulating attributes.
wwwroot/scripts/dialogJsInteropt.js
export function showDialog(element, parm) {
return element.showModal();
}
export function closeDialog(element, parm) {
return element.close();
}
Dialog.razor
<CascadingValue Value=#this IsFixed=true >
<dialog #ref="#dialogElement" #attributes=#CapturedAttributes>
#if(visible)
{
#ChildContent
}
</dialog>
</CascadingValue>
Dialog.razor.cs
public partial class Dialog : ComponentBase, IAsyncDisposable
{
private readonly Lazy<Task<IJSObjectReference>> moduleTask;
private ElementReference dialogElement;
private bool visible = false;
public Dialog()
{
moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>(
identifier: "import",
args: "./scripts/dialogJsInterop.js")
.AsTask());
}
[Inject]
private IJSRuntime jsRuntime { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter(CaptureUnmatchedValues = true)]
public Dictionary<string, object> CapturedAttributes { get; set; }
public async ValueTask ShowDialogAsync()
{
var module = await moduleTask.Value;
await module.InvokeVoidAsync("showDialog", dialogElement);
visible = true;
}
public async ValueTask CloseDialogAsync()
{
var module = await moduleTask.Value;
await module.InvokeVoidAsync("closeDialog", dialogElement);
visible = false;
}
public async ValueTask DisposeAsync()
{
if (moduleTask.IsValueCreated)
{
var module = await moduleTask.Value;
await module.DisposeAsync();
}
}
}
A this stage you have a dialog that works.
I added the following components to make it more convenient.
Note: I do use bootstrap from here forward for styling, this could be changed easily to tailwind for example.
DialogCloseButton.razor
<button #attributes=CapturedAttributes #onclick=#CloseDialog />
DialogCloseButton.razor.cs
public partial class DialogCloseButton : ComponentBase
{
[CascadingParameter]
public Dialog Dialog { get; set; }
[Parameter(CaptureUnmatchedValues = true)]
public Dictionary<string, object> CapturedAttributes { get; set; } = new Dictionary<string, object>
{
{ "class", "btn btn-close" }
};
private async Task CloseDialog() => await Dialog.CloseDialogAsync();
}
DialogCloseButton.razor.css
.btn:focus {
box-shadow: none;
}
DialogLayout.razor
<div class="d-flex flex-row justify-content-between border-bottom border-1">
<div class="flex-fill p-1 ps-3 fw-bolder user-select-none app-gradient text-white">
#Header
</div>
<div class="p-1">
<DialogCloseButton />
</div>
</div>
<div class="p-3">
#Content
</div>
DialogLayout.razor.cs
public partial class DialogLayout
{
[Parameter]
public RenderFragment Header { get; set; }
[Parameter]
public RenderFragment Content { get; set; }
}
Usage :
<Dialog #ref=#dialog class="p-0 border rounded shadow">
<DialogLayout>
<Header>
<MessagesIcon Size=16 /> Add Message
</Header>
<Content>
<MessageFormView />
</Content>
</DialogLayout>
</Dialog>
<button class="btn btn-outline-success" #onclick=#OpenDialog>Add Message</button>
#code {
private Dialog dialog;
...
private async Task OpenDialog() => await dialog.ShowDialogAsync();
}
Here is a very minimal example of what you ask (I put everything in the index.razor file, but you can use CSS isolation and a dedicated .cs file for all the content inside the #code{} part.
#page "/index"
<style>
.active {
display: block;
}
.inactive {
display: none;
}
</style>
<a class="btn btn-login" #onclick="RedirectPage" >Log in</a>
<div class="#PopupClass">
Error: #ErrorText
</div>
#code{
bool InvalidLogin {get; set;} = false;
string PopupClass => InvalidLogin ? "active" : "inactive";
public string ErrorText { get; set; } = "Example of exception";
private void RedirectPage()
{
this.InvalidLogin = !this.InvalidLogin;
}
}
Of course you will need to appropriate yourself this example in order to implement more concrete business logic.
My Alert class is
namespace RazorComponents.Models
{
public class Alert
{
public string Title { get; set; }
public string Text { get; set; }
public bool Dismissible { get; set; }
}
}
My AlertComponent is
namespace RazorComponents.Test
{
public partial class AlertComponent : ComponentBase
{
public Alert Alert { get; set; }
}
}
with the following view
#using Models
<div class="alert alert-warning alert-dismissible fade show" role="alert">
<strong>#Alert.Title</strong> You should check in on some of those fields below.
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
Which returns a compile time error
C# An object reference is required for the non-static field, method, or property at #Alert.Title
This is normal but how can i pass the Alert class as a model within the view?
At least in MVC we had the option of #model Alert and pass the model through
async Task<IViewComponentResult> InvokeAsync
You need to add a code block to your component like this:
#code
{
[Parameter]
private Alert Alert { get; set };
}
Add the [Parameter] attribute only if you want to set the value from the outside (from another component).
You need to add the [Parameter] attribute to your Alert propriety:
namespace RazorComponents.Test
{
public partial class AlertComponent : ComponentBase
{
[Parameter]
public Alert Alert { get; set; }
}
}
To use your component, just pass your alert as parameter like below:
<AlertComponent Alert="Alert"/>
#code
{
public Alert Alert {get; set;}
}
Define a code behind class:
AlertComponent.razor.cs
public partial class AlertComponent
{
[Parameter]
public Alert Alert { get; set; }
[Parameter]
public EventCallback Close { get; set; }
}
You'll need here: using Microsoft.AspNetCore.Components;
Note: The EventCallback Close parameter is used to call the method that opened the alert to close it.
Define the view part of the AlertComponent
AlertComponent.razor
<div class="alert alert-warning alert-dismissible fade show" role="alert">
<strong>#Alert.Title</strong> <div>#Alert.Text</div>
<button #onclick="#(() => Close.InvokeAsync())" type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
#code {
}
Usage:
#page "/"
#if (alert != null)
{
<AlertComponent Alert="alert" Close="Close" />
}
<button #onclick="ShowAlert">Display alert</button>
#code
{
private Alert alert;
private void ShowAlert()
{
alert = new Alert() { Title = "My alert", Text = "You are being alerted", Dismissible = true };
}
private void Close()
{
alert = null;
}
}
When you click on the "Display alert" button, a new instance of Alert is created, the Page component is re-render, this time the alert != null is true, and thus the AlertComponent is rendered. When you click on the "Close button" in the AlertComponent, the Close method defined in the Index component set the value of the alert varaible to null, after which the Index component is re-rendered, this time the AlertComponent is not re-rendered as the value of the alert variable is null.