How do you display different Components when selecting MudRadio's - c#

I would like to implement the functionality when clicking a MudRadio button (in a MudDialog), it should display a different component in the the MudDialog.
I searched for an example and couldn't find what I'm looking for

You need to have a MudRadioGroup to contain your MudRadio buttons. On the MudRadioGroup you have the #bind-SelectedOption event callback exposed. You can set a switch statement on that to dynamically render your other components.
Here is a sample project to accomplish what you're trying to do:
https://try.mudblazor.com/snippet/wOmRucPSpMDbtnAw
ParentComponent.razor
<MudForm>
<MudRadioGroup #bind-SelectedOption="#SelectedOption">
<MudRadio Option="#("Add")" Color="Color.Primary">Primary</MudRadio>
<MudRadio Option="#("Radio 2")" Color="Color.Secondary">Secondary</MudRadio>
<MudRadio Option="#("Radio 3")">Default</MudRadio>
<MudRadio Option="#("Radio 4")" Color="Color.Primary" Disabled="true">Disabled</MudRadio>
</MudRadioGroup>
</MudForm>
#switch(SelectedOption)
{
case "Add":
<TestComponent />
break;
}
<div class="d-flex align-center">
<MudButton Variant="Variant.Outlined" OnClick="Reset">Reset</MudButton>
<MudText Class="ml-4">Selected Option: #SelectedOption</MudText>
</div>
#code {
public string SelectedOption { get; set; }
private void Reset()
{
SelectedOption = null;
}
}
TestComponent.razor
<h1>Hi from: TestComponent</h1>

Related

Is there a way to cache RenderFragment output?

Is it possible to cache the output of a RenderFragment in Blazor WebAssembly?
Specifically, this is to retain components shown intermittently without rendering them to the browser. With "rendering to the browser" here I mean outputting HTML to the browser.
I am trying to get this to work to improve performance in a library I am writing where two-dimensional data is shown in the browser. The resulting grid is virtualized to prevent having tens of thousands of elements in the DOM since having that many elements results in a degraded experience.
When virtualizing, elements outside the view are not rendered only to be rendered when they shift into view.
The caching mechanism should preserve the HTML output of Razor Components such that the components can be removed from and added to the DOM without having to be reinitialized and rerendered.
Currently, I have not found a way to achieve this.
A basic set-up to reproduce what I have tried so far is as follows:
create a Blazor WebAssembly project using the default template without hosting.
Add a Razor Component with the name ConsoleWriter.razor to Shared and set the contents as follows:
<h3>ConsoleWriter #Name</h3>
#code {
[Parameter]
public string? Name { get; set; }
protected override void OnInitialized() {
Console.WriteLine($"ConsoleWriter {this.Name} initialized");
}
protected override void OnAfterRender(bool firstRender) {
if(firstRender) {
Console.WriteLine($"ConsoleWriter {this.Name} rendered");
} else {
Console.WriteLine($"ConsoleWriter {this.Name} re-rendered");
}
}
// Trying to stop rerendering with ShouldRender.
// Does not stop the rendering in scenario's where the component has just been initialized.
protected override bool ShouldRender() => false;
}
Replace the contents of Index.razor with the following code:
#page "/"
<p>Components are showing: #showComponents</p>
<button #onclick="() => this.showComponents=!this.showComponents">Toggle</button>
#* Here the components are in the DOM but can be hidden from view, still bogging down the DOM if there are too many. *#
<div style="#(this.showComponents ? null : "display:none;")">
<ConsoleWriter Name="OnlyHidden" /> #* Does not intialize or rerender when showComponents is toggled *#
</div>
#* Here the components are not in the DOM at all when hidden, which is the intended scenario but this initializes the components every time they are shown. *#
#if(this.showComponents) {
<ConsoleWriter Name="Default" /> #* Intializes and rerenders when showComponents is toggled *#
#consoleWriter #* Intializes and rerenders when showComponents is toggled *#
<ConsoleWriter #key="consoleWriterKey" Name="WithKey" /> #* Intializes and rerenders when showComponents is toggled *#
}
#code {
private bool showComponents = false;
private object consoleWriterKey = new object();
private RenderFragment consoleWriter { get; } = builder => {
builder.OpenComponent<ConsoleWriter>(0);
builder.AddAttribute(1, nameof(ConsoleWriter.Name), "RenderFragment");
builder.CloseComponent();
};
}
When running the project and checking the browser console, you can see which components report being reinitialized or rerendered.
The components can be toggled by clicking the button.
Unfortunately, all of those being removed from and added to the DOM report back whenever being toggled to be shown despite their content never changing.
Does anyone have another idea how to approach this?

Blazor, how can I trigger an "enter key event" on an input tag to call method that accepts string parameters

I have a razor component with the following input. I currently use the "GetCourses" method to retrieve a list of courses, and everything works as expected upon button click. I would like to, however, be able to type in some string into the searchbox, and upon hitting the "enter" key, call the same "GetCourses" method. I have duplicated the "GetCourses" method and modified it to work with the #OnKeyDown event, but it does not work. In the debugger, the #OnKeyDown event is triggered with each key press, but it never binds a value to "this.inputValue." I want to capture all of the string characters in the searchbox, not one at a time, and why is it not binding?
<div>
<input type="text"
class="searchbox"
name="user"
placeholder="Search by course name or course ID"
#bind="#this.inputValue"
#onkeydown="#GetCoursesbyKey" />
</div>
<span>
<button class="searchbtn"
#onclick="#(T => GetCourses(inputValue))">
Search
</button>
</span>
#code {
public string inputValue { get; set; }
private async Task GetCoursesbyKey(KeyboardEventArgs? e)
{
var search = this.inputValue;
if (e.Code == "Enter" || e.Code == "NumpadEnter")
{
if (search != null)
{
//perform some logic
}
else
{
//perform some logic
}
//wait on some methods here
}
}
private async Task GetCourses(string search)
{
var search = this.inputValue;
if (search != null)
{
//perform some logic
}
else
{
//perform some logic
}
//wait on some methods here
}
}
Making this small change made the feature work perfectly. I do not have the technical analysis as to why, other than these functions accomplish what I was attempting to do.
<div>
<input type="text"
class="searchbox"
name="user"
placeholder="Search by course name or course ID"
#bind-value="#this.inputValue"
#bind-value:event="oninput"
#onkeydown="#GetCoursesbyKey" />
</div>

Dynamically change the Body for a component in Blazor

I'm trying to achive a search functionality for Blazor-server where the idea is to use it anytime on the site by typing on a search box which causes the page to change the #Body for a component that shows the results of the search.
Currently I'm able to do the search well on the MainLayout but this is by having already a list there and the Body component either below or on top. What I need is to only show the List AFTER I input something on the search bar and to replace it with the Body.
Here is what works but whithout the issue I am having.
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search" #oninput="(ChangeEventArgs e)=>SearchHandler(e)" />
<BrowseListShared #ref="BrowseListShared" />
#Body
code{
public string Searched { get; set; }
protected BrowseListShared BrowseListShared;
public void SearchHandler(ChangeEventArgs e)
{
Searched = e.Value.ToString();
BrowseListShared.UpdadeMe(Searched);
}
}
And this is my attempt at trying to make the replacement which gives me the error "Object reference not set to an instance of an object.", the Error shows when I type something in the search box.
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search" #oninput="(ChangeEventArgs e)=>SearchHandler(e)" />
#if (setVisible){
<BrowseListShared #ref="BrowseListShared" />
}else{
#Body
}
code{
public string Searched { get; set; }
protected BrowseListShared BrowseListShared;
private bool setVisible=false;
public void SearchHandler(ChangeEventArgs e)
{
if (e != null && e.Value.ToString() != ""){
setVisible = true;
}else{
setVisible=false;
}
Searched = e.Value.ToString();
BrowseListShared.UpdadeMe(Searched);
}
}
Hope someone can give me some direction to deal with this, thank you.
Edit:
Adding if(BrowseLit != null) to avoid error does make it work with some issues.
1- the first character makes so it shows just the list without the search because on the first character the code doesnt have the reference yet for the BrowseListShared so it skips the BrowseListShared.UpdateMe for the first tipe.
2- On deleting the text in the box completely until its blank and typing again will cause this error 'Cannot access a disposed object.'
There shouldn't be an issue to add a small if-block, the following is a basic concept that works for me:
<button #onclick="#( () => test = !test )">test</button>
#if (!test)
{
#Body
}
else
{
<span>some other search content - use a component here
and pass the data as a parameter to it, and its OnParametersSetAsync
can fetch needed data: #test</span>
}
#code{
bool test { get; set; }
}
I would also suggest you try using parameters for the search details instead of a reference.
If you want to show a particular page with search results, you can consider navigating the user to that page (e.g., pass the search query as a route parameter to it) - then it will render only what you want in the #Body - which can range from nothing, to search results, to a lot of other things.

How I can Load another Razor Component into a Razor Component by a Button-click?

I just want to load a razor component into another razor component when user click search button then I want to show search razor component (page) into a hidden div when the user click hide button then it will be hidden. like inline popup.
Like this:
Main Component:
#page "/test"
<button #onclick="(() => ShowComponent = true)">Show</button>
<button #onclick="(() => ShowComponent = false)">Hide</button>
#if (ShowComponent)
{
<ShowHideComponent></ShowHideComponent>
}
#code {
bool ShowComponent { get; set; } = false;
}
ShowHideComponent.razor:
<div>Show Or Hide This</div>

Two way data/event binding w/ non strings (Blazor)

Is it possible to two way bind or bind to an event in Blazor w/ non strings? I have done this with text without an issue but any other type of object is causing me issues.
For example, I have a method that executes when inputting text in a box which is based on the value inputted as well as several other inputs on the form.
<InputNumber step=".01" class="form-control form-control-xs" #bind-Value="#Salary" #bind-Value:event="onkeydown"/>
private decimal salary {get; set;}
public decimal Salary
{
get
{
return salary;
}
set
{
salary = value;
CalculationHere();
}
}
When I do this, I get the below error:
I have also tried passing it in as a parameter like so:
#oninput="#((ChangeEventArgs __e) => CalculationHere(Convert.ToDecimal(__e.Value)"
This also does not work as it causes an error when the textbox is empty and doesn't fire for all inputs (have tried on keydown as well). There are also a lot of parameters so if possible I'd like to avoid this.
I should also note that when I run this project, set a breakpoint in the method being called, and bind like the below, it DOES work. However, removing the breakpoint stops it from working. This has left me VERY confused.
<InputNumber step=".01" class="form-control form-control-xs" #bind-Value="#Salary" #oninput="(() => CalculationHere())"/>
Is there a best practice regarding this? I'm new to web development and Blazor itself is very new so I'm not sure what the best route to go here is... Any advice? Thanks!
When you tell Blazor that it should update the value of a variable via events such as onkeydown, it does not know what to do with the event args provided to it. To achieve a two-way binding in such a case, you need to do the binding manually.
Add an #oninput directive to your InputNumber Component with the value "#((e) => #employee.Salary = Convert.ToDecimal(e.Value))"
Your InputNumber Component should look like this:
<InputNumber step=".01" class="form-control form-control-xs" #bind-Value="#employee.Salary" #oninput="#((e) => #employee.Salary = Convert.ToDecimal(e.Value))" />
Now, whenever the input of your text box changes, the input event is triggered, and the ChangeEventArags is passed to you, you extract the value, convert it to decimal and assigned it to the #employee.Salary property.
This answer could be deduced from my first answer where I use
#oninput="#((e) => CalculationHere(e))"
to call the CalculationHere
Hope this helps...
The InputNumber component should be embedded in the EditForm Component whose Model attribute is set to your model employee
You should not add the #bind-Value:event="onkeydown". What for ? The default and the correct event for the binding is the onchange event and it deals appropriately with updating the Salary property.
Put this code in the Index page and run
<EditForm Model="#employee" OnValidSubmit="#HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<InputText id="name" #bind-Value="#employee.Name" />
<!-- <InputNumber step=".01" class="form-control form-control-xs" #bind-Value="#employee.Salary" /> -->
<InputNumber step=".01" class="form-control form-control-xs" #bind-Value="#employee.Salary" #oninput="#((e) => CalculationHere(e))" />
<button type="submit">Submit</button>
</EditForm>
<p>Salary: #employee.Salary</p>
<p>After calculation: #calculation</p>
#code{
decimal calculation;
Employee employee = new Employee() { Name = "Davolio Nancy", Salary = 234 } ;
public class Employee
{
public string Name { get; set; }
private decimal salary { get; set; }
public decimal Salary
{
get
{
return salary;
}
set
{
salary = value;
//CalculationHere();
}
}
}
private void HandleValidSubmit()
{
}
void CalculationHere(ChangeEventArgs e)
{
calculation = Convert.ToDecimal(e.Value) * Convert.ToDecimal(1.2);
}
}

Categories