0

In my blazor app,

...
    <td>
        <input type="text" style="border:none;" @bind="todo.Title" />
    </td>
...

How can I get access in the @code section of the text changed value and the todo item that is related to it? Is a "after change is bound to the todo" event I can hook into to?

Currently, I can get the change event and it has the changed value but I don't have access to the todo item related to it. Or I can get access to todo item but I don't have access to what the text changed value is.

@page "/todo"

<pagetitle>Todo</pagetitle>

<h1>Todo (@todos.Count(todo => !todo.IsDone))</h1>

<table>
    @foreach (var todo in todos)
    {
        <tr>
            <td>
                <input type="checkbox" @bind="todo.IsDone" />
            </td>
            <td>
                <input type="text" style="border:none;" @bind="todo.Title" />
                @todo.RsDisplay
            </td>
        </tr>
    }
</table>

<input placeholder="Something todo" @bind="newTodo" />
<button @onclick="AddTodo">Add todo</button>

@code {
    private List<TodoItem> todos = new();
    private string? newTodo;

    private void AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(newTodo))
        {
            todos.Add(new TodoItem { Title = newTodo, RsDisplay = "test" });
            newTodo = string.Empty;
        }
    }
}
Rod
  • 14,529
  • 31
  • 118
  • 230
  • Do you mean you want to add `new TodoItem` to the table?If so,I think you need to use js,you can try to refer to the [link](https://stackoverflow.com/questions/62272040/refresh-html-table-data-using-blazor-and-c-sharp). – Yiyi You Oct 19 '22 at 08:19

4 Answers4

1

Just add @bind-value with @onchange as below.

<input type="text" style="border:none;" @bind-value="todo.Title" @onchange="YouOnChangeEventMethod" />

this will work for you.

Kiran Joshi
  • 1,758
  • 2
  • 11
  • 34
1

Copy and test...

@page "/"

<h1>Todo (@todos.Count(todo => !todo.IsDone))</h1>

<div class="d-flex col col-lg-3 mb-4">
    <input placeholder="Enter Task" @bind="newTodo" />
    <button class="btn btn-success" @onclick="AddTodo">
        Submit
    </button>
</div>

<table>
    @foreach (var todo in todos)
    {
        <tr>
            <td>
               <input type="checkbox" checked="@todo.IsDone" @onchange="@(()=>CheckboxChecked(todo))" />
            </td>
            <td>
                <input type="text" style="border:none;" @bind="todo.Title" />

            </td>
        </tr>
    }
</table>
@* For display's purpose'*@

@foreach(var task in todos)
{
    <div>@task.Title: @task.IsDone.ToString() </div>
}


@code {
    private List<TodoItem> todos = new();


#nullable enable
    private string? newTodo;
#nullable disable

    private async Task AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(newTodo))
        {
            TodoItem newTaskItem = new  TodoItem
            {
                // In a real world app TaskID will be provided by a 
                // datbase call to add the task to a database table
                Title = newTodo,
                IsDone = false
            };

            todos.Add(newTaskItem);
            newTodo = string.Empty;

        };

        await Task.Delay(1);

    }

    private async Task CheckboxChecked(TodoItem task)
    {
        task.IsDone = !task.IsDone;

        todos.Where(t => t == task);

        await Task.Delay(1);
    }

    public class TodoItem
    {

        public int TaskID { get; set; }
#nullable enable
        public string? Title { get; set; }
#nullable disable
        public bool IsDone { get; set; }

    }
}

Note: In a real world app the list of todo items should be populated from a persistent store, and saved into it when a user choose to save the changes by clicking a "Save" button.

Note that the list of todo items is bound to an input Html element through the application of two-way binding. If you want to apply some changes or whatever before the binging takes place, you can apply the binding in a different way. Do the following:

Change:

<input type="text" style="border:none;" @bind="todo.Title" />

To:

<input type="text" style="border:none;" value="@todo.Title" @onchange="@((args) => OnChange(args, todo))" />

And add this method:

private void OnChange(ChangeEventArgs args, TodoItem todo)
    {
        // Code here to do interesting things...
        // ......
        // Bind the passed value to the current TodoItem object
        todo.Title = args.Value.ToString();
    }
enet
  • 41,195
  • 5
  • 76
  • 113
0

Here's a way I figured out to accomplish what I needed.

Highlight:

<td><input @bind=item.Key @onclick="(() => OnRowClick(item))" @oninput="OnInputChanged" /></td>

Entire code:

@page "/todolist"

@using System.Threading

@inject WidgetService _service


<h1>TodoList</h1>

@if (worksheet is null)
{
    <p>Loading...</p>
}
else
{
    <table class="table table-striped">
        <thead>
            <tr>
                <th>Id</th>
                <th>Key</th>
                <th>ValueToGet</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var item in worksheet)
            {
                <tr>
                    <td>@item.Id</td>
                    <td><input @bind=item.Key @onclick="(() => OnRowClick(item))" @oninput="OnInputChanged" /></td>
                    <td>@item.ValueToGet</td>
                </tr>
            }
        </tbody>
    </table>
}


@code {
    IEnumerable<WidgetA> worksheet;
    private WidgetA _selectedRow;

    private CancellationTokenSource src = new CancellationTokenSource();

    private async Task OnInputChanged(ChangeEventArgs args)
    {
        int.TryParse(args.Value.ToString(), out int changedValue);

        if (changedValue > 5 && changedValue != _selectedRow.Key)
        {
            src.Cancel();
            src = new CancellationTokenSource();
            await Task.Delay(1500, src.Token).ContinueWith(task => DoSomeWork(task, changedValue), src.Token);
        }
    }

    private void DoSomeWork(Task task, int changedValue)
    {
        worksheet.Where(x => x.Id == _selectedRow.Id).First().Key = changedValue;
        _service.AddValueLookup(worksheet);
    }

    public void Dispose()
    {
        src.Dispose();
    }

    private void OnRowClick(WidgetA widget)
    {
        _selectedRow = widget;
    }

    protected override async Task OnInitializedAsync()
    {
        worksheet = await _service.LoadWidgetAWorksheet();
    }


}
halfer
  • 19,824
  • 17
  • 99
  • 186
Rod
  • 14,529
  • 31
  • 118
  • 230
  • 1
    That's not the way to do that... I've added some code to my answer to demonstrate how to do that. – enet Oct 19 '22 at 19:22
-1

@oninput does not work with razor, you can use @(IsPostBack ? "oninput" : null) instead. Or you can use change event and keep @bind. @(IsPostBack ? "onchange" : null)

Or you can use ajax for that. Ajax post can also do it and razor works.

@Ajax.ActionLink("click me", "Click", new {}, new AjaxOptions()
{
    HttpMethod = "post"
})