仅在用户停止键入后

时间:2018-12-21 16:16:23

标签: javascript ajax jsf jsf-2 autocomplete

我正在基于用户在<h:inputText />中输入的键入事件(onkeyup),通过数据库搜索来开发一种自动完成解决方案。

问题是我有两个先决条件,应用程序必须遵守这些先决条件才能进行服务器端搜索:

  1. 通知的文本为> = 3和<= 6个字符
  2. 用户停止键入了几毫秒(防止大量搜索导致服务器超载)

如果没有第2项,则很简单,只需添加onkeyup="return checkInputs(this.value)之类的内容,并在每次输入的文本长度为<3或> 6时返回false即可。

但是要参加第2项),看来我应该使用Javascript setTimeout ,在这种情况下,我使用了回调,即,我不能使用任何返回值来满足onkeyup事件我的输入文字。

几天来我一直在努力解决这个问题,而且我没有任何线索可以解决这个问题,只是因为我的实际堆栈受到限制(JSF 2.1而没有任何其他库,例如PrimeFaces或OmniFaces和Jquery 1.3)

HTML代码:

<h:inputText id="text" alt="text"
                maxlength="6" size="6" title="text"
                value="#{managedBean.searchText}"
                onkeyup="return checkInputs(this.value)">
                <f:ajax listener="#{managedBean.doSearch}" execute="@this"
                    event="keyup" render="form:searchResult" />
            </h:inputText>

Javascript:

function checkInputs(value) {

let cnsAnterior = sessionStorage.getItem('previousValue');
sessionStorage.setItem('previousValue', value);

if((value.length >= 3 && value.length <= 6) && (previousValue != value)){       
    return true;
}

return false;

}

我找到了at this website的一些摘要,以防止在用户停止键入之前进行搜索:

// Get the input box
var textInput = document.getElementById('test-input');

// Init a timeout variable to be used below
var timeout = null;

// Listen for keystroke events
textInput.onkeyup = function (e) {

    // Clear the timeout if it has already been set.
    // This will prevent the previous task from executing
    // if it has been less than <MILLISECONDS>
    clearTimeout(timeout);

    // Make a new timeout set to go off in 800ms
    timeout = setTimeout(function () {
        console.log('Input Value:', textInput.value);
    }, 500);
};

使用JSF,有什么办法可以参加这两个条款吗?仅当用户键入至少3个字符并且在几毫秒内停止键入时,服务器才搜索吗?

谢谢。

2 个答案:

答案 0 :(得分:1)

由于您尚未使用JSF 2.2或更高版本,并且还不能使用PrimeFaces(仅用于其p:ajax),因此您确实需要一些“解决方法”(实际上,这不是解决方法,但对于JSF 2.1好的解决方案)。

幸运的是,许多所谓的“现代” javascript UI粉丝都忘记了这一点,JSF客户端只是html,javascript和ajax。就像任何其他框架中的其他ajax一样,JSF ajax也可以使用javascript处理,并且其基本ajax处理可以以一种基本的javascript方法来覆盖,如Aborting JSF Ajax Request from jsf.ajax.addOnEvent()所示。

我以此为基础,并将其与一些可能需要创造性的基本javascript超时处理结合在一起。

它利用该选项通过超时将原始事件参数传递给非匿名javascript函数。就像在How can I pass a parameter to a setTimeout() callback?中一样。最难的部分是找出如何传递“参数”。事实证明,您甚至可以将multiple individual arguments传递给setTimeout而不是一个'arguments' argument

看起来很多代码,但是很多是注释/日志记录(可以删除),并且可以减少一些变量。

if (jsf) { // test if the 'jsf' javascript is available

    var originalRequest = jsf.ajax.request; // 'save' the original ajax handling
    var timeouts ={}; // keep an 'hash' of timeouts so it can be used for lots of
                      // different elements independently based on element id

    jsf.ajax.request = function(source, oevent, options) {

        // check for a 'delayedEvent' class on an input and only apply delay to those that have one
        delayed = document.getElementById(oevent.target.id).classList.contains("delayedEvent");
        if(delayed) {

            // check if an existing timer for this element is already running and clear it
            // so it will never fire but will be 'superseded' by a new one
            var timeout = timeouts[oevent.target.id];
            if (timeout != undefined) {
                console.log('Delayed event cleared: ' , arguments, timeout);
                clearTimeout(timeout);
            } 

            // create a new timeout with a 350 ms delay. Making the delay 'dynamic' could 
            // be done by creating 'composite' classnames like 'delayEvent500' and delayEvent250'. 
            // Or putting both via html 5 data attributes on a container elelement... 
            // Be creative
            timeout = setTimeout(applyOriginalEvent, 350, source, oevent, options);
            console.log("Delayed event created: ", arguments, timeout);
            timeouts[oevent.target.id]=timeout;
        } else {
            console.log('Direct event fired: ' , arguments);
            originalRequest.apply(null, arguments); // apply the request direcyly
        }
    };


    function applyOriginalEvent(source, oevent, options) {
        var timeout = timeouts[oevent.target.id];
        console.log('Delayed event fired: ' , arguments, timeout);
        // fire an ajax request with the original arguments;
        originalRequest.apply(null, arguments);
        // remove the timeout from the list
        delete timeouts[oevent.target.id];
    }

};

只需确保在加载jsf.js之后就加载了该文件。我在本地和通过浏览器开发人员控制台在Omnifaces展示柜上进行了测试。

我没有进行测试的是您现有的“长度”检查,但这些检查可能会保持不变。

答案 1 :(得分:-1)

要解决此问题,恕我直言,您需要在JSF堆栈之外进行思考。在Angular中,这是一种常见情况,使用RxJS中的debounceTime运算符可以解决。例如:

import { AbstractControl } from '@angular/forms';
import { debounceTime, switchMap, map, first } from 'rxjs/operators';
@Injectable({ providedIn: 'root'})
export class UserNotTakenValidatorService {
    constructor(private signUpService: SignUpService) {
}
checkUserNameTaken() {
    return (control: AbstractControl) => {
        return control.valueChanges
            .pipe(debounceTime(300))
            .pipe(switchMap(userName => this.signUpService.checkUserNameTaken(userName)))
            .pipe(map(isTaken => isTaken ? {userNameTaken: true} : null ))
            .pipe(first());
        }
    }
}

在这种情况下,仅在用户停止键入300毫秒后才调用该服务。

稍作研究,我发现RxJS和Java集成在一起。签出:https://www.baeldung.com/rx-java。如果这种集成由于某种原因没有帮助,我将尝试将RxJS库导入项目。