onChange event not firing Blazor InputSelect - c#

I have the following Code for an InputSelect
<InputSelect class="form-control form-control form-control-sm"
placeholder="Role"
disabled="#IsReadOnly"
#bind-Value="Model.Role"
#onchange="#RoleChanged">
<option value="Member">Member</option>
<option value="Admin">Admin</option>
<option value="Pioner">Pioneer</option>
<option value="Retailer">Retailer</option>
</InputSelect>
And for the Code:
bool ShowCreated;
bool ShowUpdated;
bool IsReadOnly;
string SelectedRole;
public EditForm AccountForm;
public Multi.Dtos.RegistrationDto Model = new Dtos.RegistrationDto() { Role = "Member" };
public void RoleChanged(ChangeEventArgs e)
{
SelectedRole = e.Value.ToString();
Console.WriteLine(e.Value);
}
The RoleChange function is not invoked when i try to select a new item. Can someone point out what wrong with this. The value of the proper has changed but the Event was not called.

Note:
InputSelect is a component element, not HTML element, which is why you cannot
apply to it the #onchange compiler directive. This directive is applied to
elements, usually but not necessarily with the value attribute to form the so-
called two way data-binding.
#bind-Value is a compiler directive directive instructing the compiler to
produce code that enable two-way data binding between components. Applying
#bind-Value to the InputSelect component requires you (already done in this
case by the Blazor team) to define a parameter property named Value and an
EventCallback 'delegate', conventionally named ValueChanged. Value and
ValueChanged are properties of the InputSelect component.
There are a couple of ways to do this:
<InputSelect ValueExpression="#(()=>comment.Country)"
Value="#comment.Country"
ValueChanged="#((string value) => OnValueChanged(value ))">
<option value="">Select country...</option>
<option value="USA">USA</option>
<option value="Britain">Britain</option>
<option value="Germany">Germany</option>
<option value="Israel">Israel</option>
</InputSelect>
And
private Task OnValueChanged(string value)
{
// Assign the selected value to the Model
comment.Country = value;
return Task.CompletedTask;
}
You can also implement INotifyPropertyChanged.PropertyChanged Event in your Model, as dani herrera has suggested. You do that if you want to do away with the Forms components, including the EditForm, and use the normal HTML tags instead, in which case you'll be able to do something like:
<input type="text" value="#MyValue" #onchange=OnChange"/>
Of course your model class is going to be thick, and it should communicate with the EditContext....
Hope this helps...

The request is not that old, so you may consider the following which works with EditForm
(basically hook up onto another event) => at oninput eventhandler you pass The ChangeEventArgs var, and so you can get the value to process
<InputSelect #bind-Value="#labBook.StockID" #oninput="OnLabbookStockChange" class="form-control">
#if (lstStocks != null)
{
<option value="">select...</option>
#foreach (var stock in lstStocks)
{
<option value="#stock.StockID">#stock.TestArticle.TAName</option>
}
}
</InputSelect>

The problem is the #bind attribute makes use of the #onchange event and Blazor will not allow multiple #onchange event handlers. Workarounds in the code below:
Method 1: This is the vanilla example. It shows how to wire up a dropdown using an HTML select tag when you do not require an onchange event handler.
Method 2: This is, I think, the simplest if you need 2-way data binding AND an event handler. It uses the HTML select tag (not a Blazor component) with 1-way data binding using the "value" attribute. Since the #bind attribute is not used, we are free to attach a handler to the #onchange event. This handler, as well as handling the event, also needs to populate the property in order to achieve 2-way data binding.
Method 3: If you are using the whole Blazor EditForm and InputText/InputSelect/etc components infrastructure, this method may be best for you. Without the EditContext, the example shows 2-way binding using #bind-Value. In order to handle the onchange event for any component, we add an event handler (EditContext.OnFieldChanged) for the entire form. The handler checks to see which property was changed (based on the FieldName) and fires the required event handler accordingly.
I created this page to remind me of the options for this. Hope it helps anyone who finds their way here.
#page "/dropdown"
<h2 class='#(hightlight ? "bg-info" : "")'>
User Id: #UserId
</h2>
<hr />
<h3>Using Select Tag</h3>
<p>
<label>
Method 1 (2-way binding but do not need to handle an onchange event):<br />
<select #bind="UserId">
<option value=""></option>
#foreach (var u in users)
{
<option value="#u.Key">#u.Value</option>
}
</select>
</label>
</p>
<p>
<label>
Method 2 (need to handle the onchange event; 2-way binding via the control onchange event handler):<br />
<select value="#UserId" #onchange="OnChangeUserId">
<option value=""></option>
#foreach (var u in users)
{
<option value="#u.Key">#u.Value</option>
}
</select>
</label>
</p>
<h3>Using InputSelect Tag</h3>
<EditForm EditContext="EditContext">
<p>
<label>
Method 3 (2-way binding; event handling via the EditForm EditContext.OnFieldChanged)<br />
<InputSelect #bind-Value="#UserId">
<option value=""></option>
#foreach (var u in users)
{
<option value="#u.Key">#u.Value</option>
}
</InputSelect>
</label>
</p>
</EditForm>
#code {
private EditContext EditContext;
private Dictionary<int, string> users = new Dictionary<int, string>();
private int? UserId { get; set; }
private bool hightlight { get; set; }
protected override void OnInitialized()
{
EditContext = new EditContext(users);
EditContext.OnFieldChanged += EditContext_OnFieldChanged;
}
protected override void OnParametersSet()
{
// simulate getting current data from the db (which would use async version of this event handler)
users.Clear();
users.Add(1, "Bob");
users.Add(2, "Sue");
users.Add(3, "R2D2");
UserId = 2;
base.OnParametersSet();
}
private void EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
{
if (e.FieldIdentifier.FieldName == "UserId")
{
DoStuff();
}
}
private void OnChangeUserId(ChangeEventArgs args)
{
// select tag's "value" attribute provide one-way data binding; to get 2-way,
// we need to manually set the value of the UserId based on the ChangeEventArgs
if (args.Value == null)
UserId = null;
else
UserId = int.Parse(args.Value.ToString());
DoStuff();
}
private void DoStuff()
{
hightlight = (UserId == 3);
}
}

Instead of using the onchange event. You may want to attempt using oninput event instead.

May be late to the party, but I found the easiest thing to do is use the #bind-Value="MyProperty" and then for the property call the function in the setter. Therefore, no need to use #onchanged.
so in the c# model:
private T _myProperty;
public T MyProperty
{
get => _myProperty;
set =>
{
_myProperty = value;
MyFunction();
}
}

I solved this simply using the #onclick event. It fires multiple times, but the last time it fires, its the selected item.
<div>
<EditForm Model="model">
<InputSelect #bind-Value="model.SimulatorType" #onclick="SelectionChanged">
#foreach (var value in Enum.GetValues(typeof(SimulatorType)))
{
<option>#value</option>
}
</InputSelect>
</EditForm>
</div>
#code {
class Model
{
public SimulatorType SimulatorType
{
get;set;
}
}
Model model = new Model();
private async void SelectionChanged()
{
await OnSelectionChanged.InvokeAsync(model.SimulatorType.ToString());
}
[Parameter]
public EventCallback<string> OnSelectionChanged { get; set; }
}

Related

(Blazor) Select value not updating

I have a <select> element in my page, of which I want to get the value. To do that, I've added the #bind= property. Here's the code:
<form target="#">
<label for="subject">Vak</label>
<select #bind="#selectedSubject" id="subject">
#foreach (Subject subject in Auth.User.Subjects)
{
<option value="#subject.Uuid">#subject.Name</option>
}
</select><br />
<!-- (more input fields) -->
<button class="button btn-primary mt-3" #onclick="addNote">Toevoegen</button>
</form>
#code {
private String selectedSubject { get; set; }
private String inputTitle { get; set; }
private String inputContent { get; set; }
private void addNote()
{
Console.WriteLine(selectedSubject);
}
}
This doesn't output anything, and trying to use selectedSubject results in a NullReferenceError.
The other values (inputTitle, inputContent) work as expected.
Yes, the <select> is properly being filled with <option>s.
I've tried switching to whatever <EditForm> is, but it kept giving me warnings and failed to build.
First of all, you don't need a form at all. The main purpose of EditForm is to validate inputs-- like, making sure a password is the correct format.
Also, unless you want to programmatically SET the value of the dropdown, you don't need to bind to it-- instead, use #onchange:
<select #onchange=ProcessSelection>
. . .
#code {
Subject SelectedSubject {get; set;}
async Task ProcessSelection (ChangeEventArgs args){
SelectedSubject = Auth.User.Subjects.Single(s=>s.uuid = args.Value.ToString();
// Process the selection.
}
}
This will (1) give you a place to breakpoint for debugging. (2) Let you do useful things with the SelectedSubject object more easily (like add / remove it from a list, pass it as a parameter to a display component, etc.).
My approach to the problem is different from the other answer.
Here's everything in one Razor component.
Separate your data out into a class. Note that the fields can be null (you may not want them to be).
Use EditForm with normal binding. You can then do validation.
Logic in select list to determine if no value is selected and display a select message.
Separate out the Select code into a separate RenderFragment block to keep the code cleaner. Not necessary if you like all your markup in one place.
#page "/"
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
<EditForm EditContext="editContext">
<div class="col-12 mb-3">
<label class="form-label small muted">Subject</label>
<select class="form-select" #bind="model.Subject">
#this.SubjectList
</select>
</div>
<div class="col-12 mb-3">
<label class="form-label small muted">Title</label>
<input class="form-control" #bind="model.Title">
</div>
</EditForm>
<div class="alert alert-info m-3">
<div class="m-2">
Subject : #model.Subject
</div>
<div class="m-2">
Title : #model.Title
</div>
<div class="m-2">
Content : #model.Content
</div>
</div>
#code {
//private Note model = new() { Subject="Portugal"};
private Note model = new();
private EditContext? editContext;
protected override void OnInitialized()
=> this.editContext = new EditContext(model);
private IEnumerable<string> Subjects = new List<string> { "France", "Portugal", "Spain" };
private bool noSubjectSelected => !Subjects.Any(item => item == model.Subject);
// Separates out the code into a separate render block
private RenderFragment SubjectList => (__builder) =>
{
if (noSubjectSelected)
{
<option disabled selected value="">-- Select a Subject --</option>
}
foreach (var subject in Subjects)
{
<option value="#subject">#subject</option>
}
};
// Normally separate out into own class file
public class Note
{
public string? Subject { get; set; }
public string? Title { get; set; }
public string? Content { get; set; }
}
}
I've left the subject as a string as that's what you have it as. If it's a guid you will need to adjust the Select Code to handle a Guid/String pair.
Bear in mind that source code is an instruction of rendering intent so it isn't always clear how your code will react in all cases of operation.
You have to decide whether there will be a selection at all times, or whether no material selection is also valid. For example, if you want an option of "Select a Subject" at the top of the list which is the default, so set am option with that text an a value of Guid.Empty (all zeros). When processing values - just check that the selected Guid is not equal to Guid.Empty if you want to know if a "real" subject option is selected
I use the following two approaches with InputSelect. Note that the Microsoft renderer may set the text of the select to your bound option but may not set that option to "selected"
<InputSelect #bind-Value=MyData>
<option selected value="#Guid.Empty">Select a Subject</option>
#foreach (var i in Subjects)
{
<option value=#i.Id>#i.SubjectName</option>
}
</InputSelect>
If you want finer grained control over the select, there is this binding option. As well as a value to which you bind, you also need a function which is called on change - SelectChanged - where you can run other code. you should set the bound value within this method. If the InputSelect is inside a component, the bound value should also have an EventHandler of the same name with the "Changed" text appended - you should Invoke this in your SelectChanged function. Any parent control binding to the child will have its data automatically updated and the SetStateChanged is called for you
<InputSelect Value="#MyValue" ValueChanged="#((Guid value) => SelectChanged(value))" ValueExpression="#(() => MyValue)" >
#foreach (var i in MyValues)
{
if (TheSelectedValue == i.TheValue)
{
<option selected value=#i.TheValue>#i.ValueDisplay</option>
}
else
{
<option value=#i.TheValue>#i.ValueDisplay</option>
}
}
</InputSelect>
To solve your issue with things not changing, there are two ways you can do it:
Start with a null object for your SelectedSubject, do a null check in your click, and have a dummy select option that forces you to select an item:
.
<select #onchange=ProcessChange>
<option selected disabled>Pick a subject. . . </option>
#foreach (var subject in Auth.User.Subjects) {
<option>. . . </option>
}
</select>
<button #onclick=AddSubject />
#code {
List<Subject> SelectedSubjects = new();
Subject SelectedSubject {get; set; } = null;
async Task ProcessChange (ChangeEventArgs args){. . . }
async Task AddSubject{
if (Subject is not null) SelectedSubjects.Add (SelectedSubject);
}
}
Preselect your first option in your data initialization, and then you don't NEED the dummy <option> or the null check.
.
#code{
async Task OnInitializedAsync (bool IsFirstRender){
Auth.User.Subjects = await LoadSubjectsForUser (Auth.User);
SelectedSubject = Auth.User.Subjects.First();
}
}
In your use case, I think #1 is probably better.

How to load a non-routable component in Blazer

I work on a project that uses Blazer for the front-end and Cosmos DB in the back-end. I am looping through a list of items and have a button/ anchor tag that OnClick receives a parameter. Currently, I am using the query string parameters to redirect the user from one page to another page. However, I do not want to use query string. I want to make the component non-routable and load it on a button click. Is there a way to pass the parameter on button click and load a non-routable component?
Thank you very much for your explanation, I am using .net 5. Here I am going to write a sample code, so you could help.
My project uses Azure Cosmos DB and Blazor Server.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-html -->
///////////////////////////
MoviesListComponent.razor
////////////////////////
#page "/movies"
foreach(movie in ListOfMovies){
<div> movie.ReleaseeDate</div>
<div> movie.Country</div>
<div> movie.Title</div>
<button type="button" #onClick="()=>LoadAnotherComponent(movie.title)"> on button click redirect to(or load) a non-routable compnent and send movie.title as parameter for example MovieTitleAnalysis component</button>
}
#code{
public List<Movie> ListOfMovies {get;set;}
}
on button click inside MoviesListComponent load
MovieTitleAnalysis.razor and pass the parameter. Dispose MovieListComponent
///////////////////////
MovieTitleAnalysis.razor is a non routable component
//////////////////
<div>welcome to movie Title analysis</div>
#code{
[paramter]
public string movieTitle {get;set;}
void PreformSomeOpration(){}
}
As this is conceptual - you have provided no code context - this should provide a good starting point and demo the basic concept. Comment on how well/poorly it fits!
I've pointed to the "routed" components but it will work with any component.
DynamicPage.razor
#if (ComponentType != null)
{
<DynamicComponent Parameters=this.Parameters Type=this.ComponentType />
}
else
{
#this.ChildContent
}
#code {
[Parameter] public Type? ComponentType { get; set; }
[Parameter] public RenderFragment? ChildContent { get; set; }
[Parameter] public IDictionary<string, object>? Parameters { get; set; }
}
And Index.razor
#page "/"
#*Example Menu*#
<div class="p-2 m-2">
<button class="btn btn-primary" #onclick="() => this.ChangePage(null)">Change to Index</button>
<button class="btn btn-secondary" #onclick="() => this.ChangePage(typeof(Counter))">Change to Counter</button>
<button class="btn btn-info" #onclick="() => this.ChangePage(typeof(FetchData))">Change to FetchData</button>
</div>
<DynamicPage ComponentType=this.page>
<PageTitle>Index</PageTitle>
<div>Hello World from Blazor</div>
</DynamicPage>
#code {
private Type? page = null;
private void ChangePage(Type? newPage)
=> this.page = newPage;
}

ValueChanged and ValueEpression in blazor?

I have seen the use of ValueChanged and ValueExpression in blazor component but does not have any idea wy they are used and what does ValueChanged and ValueEpression do in blazor?
If any body can explain them in simple language, it would be really a great help/
Please check the InputBase Class Properties:
ValueChanged: Gets or sets a callback that updates the bound value.
ValueExpression: Gets or sets an expression that identifies the bound value.
Generally, Razor components provide data binding features via an HTML element attribute named #bind with a field, property, or Razor expression value, we could use it to bind values to the html elements. If we are not using the bind attribute, we could use the ValueExpression and ValueChanged to update the bound value. Please refer to the following sample:
<EditForm Model="form">
<p>Current value: #form.MyProperty</p>
<InputSelect ValueChanged="#((string s) => DoThing(s))"
ValueExpression="#(()=> form.MyProperty)">
<option value="0">0</option>
<option value="100">100</option>
<option value="200">200</option>
<option value="300">300</option>
</InputSelect>
</EditForm>
#code {
MyForm form = new MyForm(); //create an model instance.
void DoThing(string s) //value changed event.
{
form.MyProperty = s; //get the current selected value and assign to the Model.
}
//define a class to get/set the dropdownlist selected value.
public class MyForm
{
public string MyProperty { get; set; }
}
}
If using the #bind attribute, the sample code as below:
<p>Current Season: #model.Season</p>
<InputSelect #bind-Value="model.Season">
#foreach (var value in Enum.GetValues(typeof(Season)))
{
<option>#value</option>
}
</InputSelect>
#code {
Model model = new Model();
class Model
{
public Season Season { get; set; }
}
enum Season
{
Spring,
Summer,
Autumn,
Winter
}
}
The screenshot as below:
Reference:
ASP.NET Core Blazor data binding

How to store data in a hidden field and the pass it between pages in razor pages

So I am trying to select a value from dropdown list and passing it as query string in a another view this works fine on get but for post I want to store it in a hidden field
<div style="margin: 30px">
<label asp-for="DropDown" class="control-label">DropDownList</label>
<select asp-items="Model.dropdown"
asp-for="targetvalue"
value ="targetvalue"
type="hidden" class="form-control">
</select>
Not sure if this is correct how do I retrieve the hidden field to use it in post
IMHO, you should bind the value to a page model field, then just generate a hidden input in your form;
<input type="hidden" asp-for="targetvalue" />
You can use any of below methods, But the recommended way is "Model Binding".
You can access the value by passing the key as an indexer to the Request.Form collection, like below:
var number = Request.Form["targetvalue"];
Add a suitable property to the PageModel and to allow model binding to apply the posted value to the property: like below:
public IndexModel : PageModel
{
[BindProperty]
public int targetvalue { get; set; }
public void OnPost()
{
// posted value is assigned to the targetvalue property automatically
}
}
For this example the Drop-down list can be rendered in different ways, But once it rendered into HTML it would look like below:
<select name="targetvalue">
<option value="">Select a number</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>

How to bind and run an async method on input change in Blazor

So I am building a Blazor component where I want to type into an input and fire an AJAX request to get filtered data from the server. I tried this
<input type="text" #bind="NameFilter" #onchange="FilterChangedAsync" />
However this results in an error
The attribute 'onchange' is used two or more times for this element.
Attributes must be unique (case-insensitive). The attribute 'onchange'
is used by the '#bind' directive attribute.
I thought about calling the method in the NameFilter property setter but in this case I can't await it. What is the proper way to achieve the desired behavior?
The #bind attribute is a compiler directive attribute instructing the compiler to create code that enables two way data binding, from a variable to the element, and from the element to the variable. Behind the scene, the compiler creates the onchange event handler whose role is to update the variable when the change event is triggered. Thus, you can't use the onchange twice. Instead you should do the following:
<input type="text" #bind="NameFilter" />
To retrieve the data entered define a property like this:
public string NameFilter { get; set; }
In that case you can add a button control with a click event handler that can access the value of NameFilter, and use it for your filtering calls, like this:
<button class="btn btn-primary" #onclick="#FilterMe">Filter Me</button>
And,
private void FilterMe()
{
var filter = NameFilter;
}
Or still better, bind the NameFilter variable to the value attribute, plus defining an event handler, like this:
<input type="text" value="#NameFilter" #onchange="FilterChangedAsync" />
But in that case it is your responsibility to update the bound variable, which you can do in the event handler itself, or using a lambada expression as the value of #onchange
private void FilterChangedAsync(ChangeEventArgs args)
{
NameFilter = args.Value.ToString();
}
This is how you update the NameFilter property with lambada expression:
<input type="text" value="#NameFilter" #onchange="#(( args ) => NameFilter = args.Value.ToString())" />
Note: The change event is triggered only when you tab out of the text box control, and this behavior may not suit your filtering requirements. The input event, on the other hand, occurs each time you type on the keyboard.
Using the input event:
<input type="text" #bind-value="#NameFilter" #bind-value:event="oninput" />
Or you can do it with an accompanying method like this:
<input type="text" value="#NameFilter" #oninput="#FilterChangedAsync" />
and
private void FilterChangedAsync(ChangeEventArgs args)
{
NameFilter = args.Value.ToString();
}
Good luck...
>= Net7
Quoting Blazor data binding get/set/after modifiers
In .NET 7 you can now easily run async logic after a binding event has completed using the new #bind:after modifier:
<input #bind="searchText" #bind:after="PerformSearch" />
#code {
string searchText;
async Task PerformSearch()
{
// ... do something asynchronously with 'searchText' ...
}
}
also useful:
<input #bind:get="Value" #bind:set="ValueChanged" />
#code {
[Parameter] public TValue Value { get; set; }
[Parameter] public EventCallback<TValue> ValueChanged { get; set; }
}
Since this is the number one answer that did not help me, I'll post the actual solution here, as the first one is named async but defacto is not.
It is important to return a Task from the async method and not void, that is the whole trick.
Code example:
<InputFile accept=".xlsx" OnChange="#LoadFile" single />
...
#code {
private ExcelData? data;
private async Task LoadFile(InputFileChangeEventArgs e)
{
data = await ExcelReader.ImportExcelFile(e.File);
}
}

Categories

Resources