Reputation: 41
Trying to bind to a multiple selection values in Blazor.
Frontend shows this:
<div class="form-group col-sm-4">
@foreach (var tagName in TagSelection)
{
<label>
@tagName.Name:
<select multiple>
@foreach (var value in tagName.Values)
{
<option value="@value.Name">@value.Name</option>
}
</select>
</label>
<br>
}
<br>
</div>
I don't even know where to begin to be able to bind to the multiple selection event. Saving question for now until further research but would appreciate any resources 😊
Thanks.
Upvotes: 0
Views: 549
Reputation: 18179
Here is a working demo to bind multiple select values(bind data to SelectedValues
in Tag
class):
Counter.razor:
@page "/counter"
<h1>Counter</h1>
<form>
<div class="form-group col-sm-4">
@{ var count = 0;}
@foreach (var tagName in TagSelection)
{
<div>
<label>
@TagSelection[count].Name:
@{ var id = "id" + count; }
<SelectMultiple uniqueID="@id" @bind-values="tagName.SelectedValues" multiple="@(true)" update="update" classNames="@("form-control")">
@foreach (var value in tagName.Values)
{
<option value="@value.Name">@value.Name</option>
}
</SelectMultiple>
@if (tagName.SelectedValues == null || tagName.SelectedValues.Count == 0)
{
<h3>No Tags selected!</h3>
}
else
{
@foreach (var item in tagName.SelectedValues)
{
<span> @item |</span>
}
}
</label>
@{count++;}
</div>
}
<br>
</div>
</form>
@code {
[Parameter]
public List<Tag> TagSelection { get; set; } = new List<Tag> {
new Tag { Name = "tag1", Values = new List<Value> { new Value { Name = "tag1_value1" }, new Value { Name = "tag1_value2" }, new Value { Name = "tag1_value3" } } } ,
new Tag { Name = "tag2", Values = new List<Value> { new Value { Name = "tag2_value1" }, new Value { Name = "tag2_value2" }, new Value { Name = "tag2_value3" } } } ,
new Tag { Name = "tag3", Values = new List<Value> { new Value { Name = "tag3_value1" }, new Value { Name = "tag3_value2" }, new Value { Name = "tag3_value3" } } }
};
void update(string newMessage)
{
StateHasChanged();
}
public class Tag
{
public string Name { get; set; }
public List<Value> Values { get; set; }
public List<string> SelectedValues { get; set; } = new List<string>();
}
public class Value
{
public string Name { get; set; }
}
}
Shared/SelectMultiple.razor:
@typeparam TItem
@inject IJSRuntime jsRuntime
<select id="@uniqueID" class="@classNames" multiple="@multiple" @onchange="OnChange" style="height: 145px;">
@if (title != null)
{
<option selected disabled value="null">@title</option>
}
<CascadingValue name="Dropdown" Value="@this">
@ChildContent
</CascadingValue>
</select>
@code {
[Parameter]
public string ID { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public List<TItem> values { get; set; }
[Parameter]
public Action<List<TItem>> valuesChanged { get; set; }
[Parameter] public EventCallback<string> update { get; set; }
[Parameter]
public string title { get; set; }
[Parameter]
public bool multiple { get; set; }
[Parameter]
public string classNames { get; set; }
[Parameter]
public string uniqueID { get; set; }
private static T ConvertByType<T>(object obj)
{
if (obj is T)
{
return (T)obj;
}
try
{
return (T)Convert.ChangeType(obj, typeof(T));
}
catch (InvalidCastException)
{
return default(T);
}
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
}
await jsRuntime.InvokeAsync<object>("dropdown.custom_select", uniqueID, String.Join(",", values), multiple);
}
public void OnChange(ChangeEventArgs e)
{
jsRuntime.InvokeVoidAsync("dropdown.onOptionSelect", DotNetObjectReference.Create(this), uniqueID);
}
[JSInvokable("onOptionSelect")]
public async Task<bool> onOptionSelect(string selectId, List<string> selectedValues)
{
try
{
if (values != null)
{
values = selectedValues.Select(x => ConvertByType<TItem>(x)).ToList();
}
else
{
values.Clear();
}
valuesChanged.Invoke(values);
StateHasChanged();
await update.InvokeAsync("Hello from ChildComponent");
return await Task.FromResult(true);
}
catch
{
return await Task.FromResult(false);
}
}
}
blazor.server.js:
window.dropdown = {
custom_select: function (id, values, multiple) {
if (values != undefined) {
if (multiple) {
if (Array.isArray(values)) {
var selectValues = values;
}
else {
var selectValues = values.split(',');
}
var selector = '#' + id + ' option';
/* Iterate options of select element */
for (const option of document.querySelectorAll(selector)) {
/* Parse value to integer */
const value = option.value;
/* If option value contained in values, set selected attribute */
if (selectValues.indexOf(value) !== -1) {
option.setAttribute('selected', 'selected');
}
/* Otherwise ensure no selected attribute on option */
else {
option.removeAttribute('selected');
}
}
}
else {
document.getElementById(id).value = values;
}
}
},
getSelectValues: function (select) {
var result = [];
var options = select && select.options;
var opt;
for (var i = 0, iLen = options.length; i < iLen; i++) {
opt = options[i];
if (opt.selected) {
result.push(opt.value || opt.text);
}
}
return result;
},
onOptionSelect: function (dotNetObject, selectId) {
var values = dropdown.getSelectValues(document.getElementById(selectId));
dotNetObject.invokeMethodAsync('onOptionSelect', selectId, values);
},
}
Add <script src="~/js/blazor.server.js"></script>
to Pages/_Host.cshtml.
result:
Upvotes: 1