【问题标题】:How to conditionally PreventDefault inside the input component in Blazor?如何在 Blazor 的输入组件中有条件地使用 PreventDefault?
【发布时间】:2020-04-22 19:36:22
【问题描述】:

我想阻止输入的默认行为(带有type="text" 的html input 标记)仅在按下输入时才发生

protected void HandleKeyPress(KeyboardEventArgs keyboardEventArgs)
{
    if (keyboardEventArgs.Key == "Enter")
    {
        // prevent default
    }     

    // rest of function  
}

我知道我可以执行@on{EVENT}:preventDefault 之类的操作,但如何在事件处理程序中有条件地执行此操作?

在 javascript 中我可以做类似的事情

function(e){
    if(e.keyCode == 13) {
      e.preventDefault();
    }
    // rest of function
}

如何在 blazor 中做同样的事情?

编辑:

我正在为一个库进行输入,但我无法更改提交按钮,我需要在我的输入组件中防止这种情况发生。

【问题讨论】:

  • 什么类型的输入?你的意思是 if (keyboardEventArgs.Key == "Enter") 而不是 if (keyboardEventArgs.Key != "Enter"),对吧?
  • @enet 对不起,我的错字。我刚刚解决了这个问题
  • 再次,什么类型的输入?文本,按钮......
  • @enet html input 标签
  • 列表很长...什么类型的输入?

标签: blazor blazor-server-side


【解决方案1】:

如前所述,您必须在 JavaScript 中执行此操作。如果你执行这段 JavaScript 代码:

window.addEventListener("load", function () {
    //This will be called when a key is pressed
    var preventDefaultOnEnterCallback = function (e) {
        if (e.keyCode === 13 || e.key === "Enter") {
            // console.log("Prevented default.")
            e.preventDefault();
            return false;
        }
    };
    //This will add key event listener on all nodes with the class preventEnter.
    function setupPreventDefaultOnEnterOnNode(node, add) {
        if (node instanceof HTMLElement) {
            var el = node;
            //Check if main element contains class
            if (el.classList.contains("prevent-default-on-enter") && add) {
                // console.log("Adding preventer: " + el.id);
                el.addEventListener('keydown', preventDefaultOnEnterCallback, false);
            }
            else {
                // console.log("Removing preventer: " + el.id);
                el.removeEventListener('keydown', preventDefaultOnEnterCallback, false);
            }
        }
    }
    //This will add key event listener on all nodes with the class preventEnter.
    function setupPreventDefaultOnEnterOnElements(nodelist, add) {
        for (var i = 0; i < nodelist.length; i++) {
            var node = nodelist[i];
            if (node instanceof HTMLElement) {
                var el = node;
                //Check if main element contains class
                setupPreventDefaultOnEnterOnNode(node, add);
                //Check if any child nodes contains class
                var elements = el.getElementsByClassName("prevent-default-on-enter");
                for (var i_1 = 0; i_1 < elements.length; i_1++) {
                    setupPreventDefaultOnEnterOnNode(elements[i_1], add);
                }
            }
        }
    }
    // Create an observer instance linked to the callback function
    // Read more: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
    var preventDefaultOnEnterObserver = new MutationObserver(function (mutations) {
        for (var _i = 0, mutations_1 = mutations; _i < mutations_1.length; _i++) {
            var mutation = mutations_1[_i];
            if (mutation.type === 'childList') {
                // A child node has been added or removed.
                setupPreventDefaultOnEnterOnElements(mutation.addedNodes, true);
            }
            else if (mutation.type === 'attributes') {
                if (mutation.attributeName === "class") {
                    console.log('The ' + mutation.attributeName + ' attribute was modified on' + mutation.target.id);
                    //class was modified on this node. Remove previous event handler (if any).
                    setupPreventDefaultOnEnterOnNode(mutation.target, false);
                    //And add event handler if class i specified.
                    setupPreventDefaultOnEnterOnNode(mutation.target, true);
                }
            }
        }
    });
    // Only observe changes in nodes in the whole tree, but do not observe attributes.
    var preventDefaultOnEnterObserverConfig = { subtree: true, childList: true, attributes: true };
    // Start observing the target node for configured mutations
    preventDefaultOnEnterObserver.observe(document, preventDefaultOnEnterObserverConfig);
    //Also check all elements when loaded.
    setupPreventDefaultOnEnterOnElements(document.getElementsByClassName("prevent-default-on-enter"), true);
});

然后,您只需将 CSS 类 prevent-default-on-enter 添加(或删除)到您想要具有此行为的输入中。

<input type="text" class="prevent-default-on-enter" />

<button type="submit" class="prevent-default-on-enter">Submit</button>

您可以即时执行此操作。无需在 Razor 代码中与 JavaScript 交互 :-)

【讨论】:

    【解决方案2】:

    要实现您的需要,您需要像这样设置组件的 html

    <input @onkeydown="KeyDown" @onkeydown:preventDefault="shouldPrevent" type="text" />
    <p>@eventText</p>
    

    您可以将 preventDefault 设置为变量,然后在您的 C# 代码中更改它

    @code {
        private bool shouldPrevent;
        private string eventText = "";
    
        private void KeyDown(KeyboardEventArgs e){
            if(e.Code is "Enter"){
                eventText = "Enter pressed";
                shouldPrevent = true;
                return;
            }
            eventText = "";
            shouldPrevent = false;
        }
    }
    

    您可以在这里查看blazorfiddle 的工作代码。

    【讨论】:

    • 这不适用于当前事件。由于 Blazor 事件循环,它将阻止 NEXT 事件,这可能不是预期的结果。
    • 它适用于当前,你可以在这里查看它blazorfiddle.com/s/dmchk7f9 它会在你输入“A”时阻止默认操作。
    【解决方案3】:

    简而言之,您不能(在 C# 中)。

    这是因为所有 JavaScript 互操作都是异步完成的,有条件地取消事件必须始终是同步的。

    当您的输入控件获得焦点时,您希望阻止您的库用户能够按 Enter 提交他们的表单,这似乎很奇怪。

    但是,您可以编写一些 javascript 来为您完成。您可以在 html 输入控件中使用 @ref=SomeVariable,这将为您提供一个 HtmlElementReference,您可以在 firstRender == true 时将其作为参数从您的组件 OnAfterRender 方法传递给您的 javascript

    你可以在这里阅读如何做那种事情https://blazor-university.com/javascript-interop/calling-javascript-from-dotnet/passing-html-element-references/

    【讨论】:

    • It seems odd you'd want to prevent your library users from being able to press enter to submit their form when your input control is focused 我正在做一个自动完成输入,要在自动完成的下拉列表中选择一个项目,用户必须按下回车键。如果我不阻止默认,它会选择项目但也会提交表单。
    • @Peter Morris,为什么他不能。我向他发布了运行良好但需要大量工作的代码。所以现在我给了他最简单的解决方案:JavaScript 嵌入 C#。
    • @enet 我认为他的意思是You can't prevent default conditionally only with blazor
    【解决方案4】:

    感谢 enet 和 Peter Morris 提供的帮助。

    经过多次尝试,似乎唯一的方法是使用 javascript

    .js

    window.preventDefaultOnEnter = function (element, remove = false) {
        var preventDefaultOnEnterFunction = function (e) {
            if (e.keyCode === 13 || e.key === "Enter") {
                e.preventDefault()
                return false
            }
        }
        if (remove) {
            element.removeEventListener('keydown', preventDefaultOnEnterFunction, false);
        }
        else {
            element.addEventListener('keydown', preventDefaultOnEnterFunction, false);
        }
    }
    

    .剃刀

    <input @ref="InputRef" />
    
    @code {
        [Inject]
        private IJSRuntime JSRuntime { get; set; }
    
        public ElementReference InputRef { get; set; }
    
        protected override async Task OnInitializedAsync()
        {
            await base.OnInitializedAsync();
            if (PreventDefaultOnEnter)
            {
                await JSRuntime.InvokeVoidAsync("preventDefaultOnEnter", InputRef);
            }
        }
    
        public override async void Dispose()
        {
            base.Dispose();
            if (PreventDefaultOnEnter)
            {
                await JSRuntime.InvokeVoidAsync("preventDefaultOnEnter", InputRef, true);
            }
        }
    }
    

    【讨论】:

    • JS 代码似乎泄露了。它向 removeEventListener 传递一个与传递给 addEventListener 的函数对象不同的函数对象实例。
    【解决方案5】:

    我在这种情况下采取的解决方案是:

    1. 创建了一个javascript 函数来处理input 元素中发生的事件
    2. 在 C# 中创建的方法会根据满足 JS 的条件来调用。

    2.1。如果触发了keyup 事件,我们希望在 C# 中将最终值设置为对象的属性

    2.2。另一方面,如果keydown 被触发并且键是我们特别感兴趣的键之一(进入/向上/向下/等),那么我们希望触发一些特定的逻辑。

    函数如下所示:

    <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>
    

    在 C# 中我们有:

    [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();
    }
    

    我觉得我不应该从另一个线程粘贴我自己的答案,所以只需在此处使用完整代码示例检查剩余答案:https://stackoverflow.com/a/66290595/1215913

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-13
      • 2020-10-23
      • 1970-01-01
      • 2016-10-31
      相关资源
      最近更新 更多