这个问题看起来很简单,但我还没有找到任何解决方案。我有一个带有onkeydown
事件的Blazor输入:
<input @onkeydown="@(e => KeyWasPressed(e))"
@onkeydown:preventDefault="@PreventDefault"
id="@GetId()"
name="@GetId()"
@bind-value="@InputValue"
@bind-value:event="oninput" />
用户应该编写文本,但使用箭头键,用户应该在列表中导航(所以我试图防止光标移动到文本的顶部和末尾(。
在JavaScript中,这可能是这样的:
function KeyWasPressed(e)
{
// key down
if (e.keyCode == 40)
{
e.preventDefault();
// some work...
}
// key up
else if (e.keyCode == 38)
{
e.preventDefault();
// some work...
}
}
如何在Blazor做到这一点?使用@onkeydown:preventDefault
,可以防止整个输入。如果将其设置为变量(@PreventDefault
(,则只能阻止下一个输入(因为第一个输入已经发生(。只是为了理解我的意思:
- PreventDefault FALSE>输入"H">将PreventDefault设置为FALSE
- PreventDefault FALSE>输入"ArrowUp">将PreventDefault设置为TRUE
- PreventDefaultTRUE>输入"i">将PreventDefault设置为FALSE
因此输入将为(|=光标(:H|>|H>|H
这意味着游标是错误的,"i"被阻止了。
有什么想法吗?谢谢你的建议。
不幸的是,目前还没有简单的解决方案。对于这种情况,如果您想回调.NET代码,您仍然需要使用JS事件处理程序,可能还需要一些互操作。
您可以在这里找到Steve Sanderson关于原因(异步处理程序(的简短评论https://github.com/dotnet/aspnetcore/issues/14517#issuecomment-559184498
另一种解决方法是将输入绑定到变量并手动更新值。(这里也指出了PreventDefault对Blazor的输入(但这可能是一条崎岖的道路。
就我个人而言,我希望看到preventDefault和stopPropagation作为传递到C#方法处理程序的EventArgs的一部分。我认为这在技术上具有挑战性。然而,它将简化许多用例。您可能想在aspnetcore上对此打开一个问题。
(晚了几年,但这可能会帮助其他人。(
drocha87指出此方法仅适用于WebAssembly(wasm(
onkeydown甚至在默认值之前启动,因此我们可以在使用@onkeydown:preventDefault=true.
之前立即禁用它。
注:
<input @onkeydown="KeyWasPressed" @onkeydown:preventDefault="preventDefault" @bind-value="@InputValue" />
@code {
bool preventDefault = false;
string InputValue = "";
private void KeyWasPressed(KeyboardEventArgs e)
{
// key down
if (e.Key == "ArrowDown")
{
preventDefault = true;
// some work...
}
// key up
else if (e.Key == "ArrowUp")
{
preventDefault = true;
// some work...
}
else
{
preventDefault = false;
}
}
}
我的解决方案如下。
在JS中创建以下方法。它将捕捉向上和向下按键。
<script>
function subscribeToChange(componentRef, inputRef) {
console.log("subscribeToChange!");
inputRef.onkeydown = function (event) {
if (event.keyCode == "38") {
event.preventDefault();
console.log("Up key pressed");
componentRef.invokeMethodAsync('invokeFromJS');
}
else if (event.keyCode == "40") {
event.preventDefault();
console.log("Down key pressed");
componentRef.invokeMethodAsync('invokeFromJS');
}
};
inputRef.onkeyup = function (event) {
componentRef.invokeMethodAsync('setValueFromJS', inputRef.value);
};
}
</script>
剃须刀:
<input @ref="Typing3" />
@HelloFromJSStr
剃须刀中:
public ElementReference Typing3;
public string HelloFromJSStr = "";
[JSInvokable("invokeFromJS")]
public async Task HelloFromJS()
{
//HelloFromJSStr += "A";
//StateHasChanged();
// do something here on UP or DOWN button presses..
}
[JSInvokable("setValueFromJS")]
public async Task SetValueFromJS(string newValue)
{
HelloFromJSStr = newValue;
StateHasChanged();
}
在我的例子中,有两种方法。触发onkeyup
的一个可以用于设置属性(HelloFromJSStr
(的最终值。如果任何其他UI元素需要重新渲染,则随后调用StateHasChanged()
方法,如示例所示。休息取决于你的想象。
剩下的代码部分是初始化:
public partial class EventsAndRendering
{
[Inject]
protected IJSRuntime JSRuntime { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
Console.WriteLine($"{this.GetType().Name}: OnAfterRenderAsync. firstRender: {firstRender}");
if (firstRender)
{
var componentRef = DotNetObjectReference.Create(this);
await JSRuntime.InvokeAsync<object>("subscribeToChange", new object[] { componentRef, Typing3 });
}
}
// HelloFromJS()
// SetValueFromJS(string newValue)
}
DotNetObjectReference
允许我们从JS中调用C#代码,而不需要使方法static
s.
因此,最终流程如下:
- 在页面第一次呈现后->我们创建了对主要组件的引用以及对inpout元素的引用
- 然后,我们使用上面提到的引用来调用
subscribeToChange
JS方法,该方法订阅在我们的输入元素上发生的事件 - 在一个keydown事件中,我们触发C#中的方法
HelloFromJS
- 在一个keyup事件中,我们触发C#中的方法
SetValueFromJS
以上只是一个例子,必须根据需要进行调整:(
灵感:https://learn.microsoft.com/en-us/aspnet/core/blazor/call-dotnet-from-javascript?view=aspnetcore-5.0#实例方法调用
编辑:考虑实现IDisposable
接口来处理.Net对象:
public void Dispose()
{
componentRef?.Dispose();
}