我正在基于用户在<h:inputText />
中输入的键入事件(onkeyup),通过数据库搜索来开发一种自动完成解决方案。
问题是我有两个先决条件,应用程序必须遵守这些先决条件才能进行服务器端搜索:
如果没有第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个字符并且在几毫秒内停止键入时,服务器才搜索吗?
谢谢。
答案 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库导入项目。