dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.36k stars 9.99k forks source link

<input> oddness when intercepting oninput events #20067

Closed aktxyz closed 4 years ago

aktxyz commented 4 years ago

https://blazorfiddle.com/s/p0pkrmtm

This sample textbox changes the letter "a" to a period "." as you type (successfully). This sample also tries to prevent the letter "x" from registering (fails). To reproduce, type abxa in the text field, the result should be

- typed       should see    actually see
=======       ===========   ===========
- a       =>  .             .
- ab      =>  .b            .b
- abx     =>  .b            .bx    <== the letter x should be filtered out
- abxa    =>  .b.           .b.  

This is similar to an issue I used to have with react.

In addition, the midstream text editing has a problem when trying to "get involved" in the input per keypress. If you type in a string, then move the cursor to the middle and try to edit, the cursor WARPS to the end of the input.

This is similar to what is described here https://stackoverflow.com/a/49648061

I have tried this with latest chrome/firefox/edge.

https://blazorfiddle.com/s/p0pkrmtm

javiercn commented 4 years ago

@aktxyz thanks for contacting us.

This is because you are using OnInputChange instead of "bind" we have special logic to handle these types of things on bind.

@SteveSandersonMS can add details here.

SteveSandersonMS commented 4 years ago

@javiercn is correct. You need to use @bind for a two-way binding if you're using Blazor Server.

With Blazor WebAssembly it makes no difference either way, since all the logic runs locally and synchronously. But with Blazor Server, there are events happening possibly simultaneously at both ends of a wire - on the client side, and on the server side - and the framework has to reconcile these events together without them clashing. The @bind mechanism tells the framework you're trying to keep these in sync and enables resolution logic. If you just use @value and @oninput, the framework doesn't know that either one feeds into the other, so it has no way to coordinate them together.

javiercn commented 4 years ago

@SteveSandersonMS I believe this is wasm, the examples are in a js fiddle

SteveSandersonMS commented 4 years ago

Ah yes good point! I realise now, having inspected the scenario in more detail, that this is one of the cases where @bind also is required in Blazor WebAssembly. Again, without @bind the framework can't track how the value output and the @oninput handler are associated.

Here's an updated implementation of the repro that works using @bind: https://blazorfiddle.com/s/4n332etu

aktxyz commented 4 years ago

should have been wasm i the subject !

fantastic, thanks for that bind example ... the cursor still warps to the end when trying to add an "a" or "x" midstream in the text ... any thoughts on that part?

aktxyz commented 4 years ago

This version is using requestAnimationFrame and jsinterop to "fix" the jumping cursor (when typing an "x" midstream)

https://blazorfiddle.com/s/tbusf92m

any thoughts on if this approach has potential problems ...

inspiration from here ... https://stackoverflow.com/questions/35535688/stop-cursor-jumping-when-formatting-number-in-react/49648061#49648061

SteveSandersonMS commented 4 years ago

@aktxyz Yes, in the cases where your code mutates the textbox value though code, Blazor has to update the .value property of the corresponding <input> element. When the .value property is written, browsers automatically move the cursor to the end.

As for whether this is the right default behavior, it's subjective. I know in your case it's undesirable, but consider cases where you programatically change the text field content to something completely different, or at least very different. It wouldn't make sense to a user that their cursor is now randomly in the middle of some new text. Or consider cases where you have a property setter that does something like add a character to the end of the string: if we preserved the cursor position (in terms of distance from start), then the cursor would now go to the second-to-last character, which would be crazy. I suppose this is why browsers move it to the end by default.

I'm filing https://github.com/dotnet/aspnetcore/issues/20072 for follow-up on this general area. In the meantime I'm glad you have your own solution!