所以这是我的可观察代码:
var suggestions =
Rx.Observable.fromEvent(textInput, 'keyup')
.pluck('target','value')
.filter( (text) => {
text = text.trim();
if (!text.length) // empty input field
{
this.username_validation_display("empty");
}
else if (!/^\w{1,20}$/.test(text))
{
this.username_validation_display("invalid");
return false;
}
return text.length > 0;
})
.debounceTime(300)
.distinctUntilChanged()
.switchMap(term => {
return $.ajax({
type: "post",
url: "src/php/search.php",
data: {
username: term,
type: "username"
}
}).promise();
}
);
suggestions.subscribe(
(r) =>
{
let j = JSON.parse(r);
if (j.length)
{
this.username_validation_display("taken");
}
else
{
this.username_validation_display("valid");
}
},
function (e)
{
console.log(e);
}
);
我遇到的问题是当输入为空时我有另一段代码基本上会返回错误:空输入'但它被返回的observable覆盖了。所以我想知道如果text.length
为0,是否有办法忽略所有可观察量,但是当文本长度不为零时重新订阅。
我已经考虑过unsubscribe
,但不知道在哪里尝试。
答案 0 :(得分:8)
也许这会奏效吗?我们的想法是在switchmap中加入空输入,以便关闭当前的observable,即在这种情况下也将其丢弃。
var res = $scope.cat.filter(function(item){
return item.parentid === categories[i].id;
});
if(res.length)
{
$scope.content.push(categories[i]);
}
答案 1 :(得分:5)
我建议在文本输入流之后移动debounceTime
调用,并在去抖动流上进行过滤。这样一个有效但过时的值不会潜入ajax调用。此外,我们可能需要取消"当一个新的,可能无效的输入发生时,我们不希望"无效"要使用正在进行的异步调用的结果覆盖的状态。 takeUntil
可以为此提供帮助。另请注意,可以使用Rx.Observable.ajax()
代替$.ajax()
。
this.username_validation_display(validationStatus)
就在subscribe内的一个地方完成。
我想它是这样的:
const term$ = Rx.Observable.fromEvent(textInput, "keyup")
.pluck("target", "value")
.map(text => text.trim())
.distinctUntilChanged()
.share();
const suggestion$ = term$
.debounceTime(300)
.filter(term => getValidationError(term).length === 0)
.switchMap(term => Rx.Observable
.ajax({
method: "POST",
url: "src/php/search.php",
body: {
username: term,
type: "username"
}
})
.takeUntil(term$)
)
.map(result => JSON.parse(result));
const validationStatus$ = Rx.Observable.merge(
term$.map(getValidationError),
suggestion$.map(getSuggestionStatus));
// "empty", "invalid", "taken", "valid" or ""
validationStatus$
.subscribe(validationStatus => this.username_validation_display(validationStatus));
// Helper functions
function getValidationError(term) {
if (term.length === 0) {
return "empty";
}
if (!/^\w{1,20}$/.test(term)) {
return "invalid";
}
return "";
}
function getSuggestionStatus(suggestion) {
return suggestion.length > 0 ? "taken" : "valid";
}
上述代码的行为在某种意义上也是不同的,即在用户输入有效输入的时间点与建议从服务器到达的时间点之间,username_validation_display
将具有空值,即除了"空","无效","采取"和"有效"状态也会有一个中间空状态,可以用UI中的某些进度指示替换。
更新:我还可以考虑一种应该具有等效行为的替代方法,但相应的代码可能看起来更简单:
const username$ = Rx.Observable.fromEvent(textInput, "keyup")
.pluck("target", "value")
.map(text => text.trim())
.distinctUntilChanged();
const validationStatus$ = username$.switchMap(username => {
const validationError = getValidationError(username);
return validationError ?
Rx.Observable.of(validationError) :
Rx.Observable.timer(300)
.switchMapTo(getAvailabilityStatus$(username))
.startWith("pending");
});
// "invalid", "empty", "taken", "valid" or "pending"
validationStatus$.subscribe(
validationStatus => this.username_validation_display(validationStatus));
// Helper functions
function getAvailabilityStatus$(username) {
return Rx.Observable
.ajax({
method: "POST",
url: "src/php/search.php",
body: {
username: username,
type: "username"
}
})
.map(result => JSON.parse(result))
.map(suggestions => suggestions.length > 0 ? "taken" : "valid");
}
function getValidationError(username) {
if (username.length === 0) {
return "empty";
}
if (!/^\w{1,20}$/.test(username)) {
return "invalid";
}
return null;
}
这样我们可以同步检查每个不同的输入。如果输入为空或无效(由getValidationError()
调用确定),我们会立即发出验证错误。否则我们立即发出"待定" status并启动一个计时器,该计时器将在300 ms后产生异步用户名可用性检查。如果在计时器结束或服务器到达可用性状态之前到达新文本,我们取消内部流并重新开始验证,这要归功于switchMap
。所以它应该与debounceTime
几乎相同。
答案 2 :(得分:1)
您可以将原始值流用作ajax调用的通知程序以中止其值。如果字段为空而没有被先前的ajax结果覆盖,这应该为您提供足够的控制来添加显示消息的附加处理。
const inputValues = Rx.Observable.fromEvent(document.getElementById("inputField"), "keyup")
.pluck("target","value")
.distinctUntilChanged()
.do(val => console.log('new value: ' + val));
inputValues
.filter(val => val.length > 0)
.switchMap(text => doAjaxCall(text)
.takeUntil(inputValues)
)
.subscribe(val => console.log('received a value: ' + val));
function doAjaxCall(inputValue) {
return Rx.Observable.of(inputValue)
.do(val => console.log('ajax call starting for: ' + val))
.delay(1000)
.do(val => console.log('ajax call returned for: ' + val));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.4.2/Rx.js"></script>
<label>input field<input type="text" id="inputField" /></label>