2017-08-22 13 views
17

Więc tutaj jest mój kod do zaobserwowania:rxjs - Zniszcz przyszłe powrocie obserwable

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

Mam problem jest, gdy wejście jest pusty Mam kolejny kawałek kodu, który zasadniczo zwraca „błąd: pusty wejście” ale zostaje on przesłonięty przez powracającego obserwowalnego. Zastanawiałem się więc, czy istnieje sposób na zignorowanie wszystkich obserwowalnych, jeśli text.length ma wartość 0, ale także ponownie zasubskrybuj, gdy długość tekstu nie wynosi zero.

Myślałem o unsubscribe, ale nie wiem, gdzie go dopasować, aby spróbować.

+0

Jak widzę już filtrowanie wartości obserwowalnych przez 'text.length> 0', więc nie powinno nic zastąpić. Aby lepiej zrozumieć swój problem, możesz podać działający fragment kodu? –

+0

@OlesSavluk Problem polega na tym, że jeśli wpiszesz pojedynczą literę, a następnie cofnę ją, to nadal będzie wysyłać żądanie przez literę i odzyskać odpowiedź. Na przykład. Wpisuję 'e', a następnie cofam. Nadal będę uzyskiwał wyniki z 'e'. Ale tak, będę musiał wymyślić sposób na stworzenie fragmentu. –

Odpowiedz

9

Może to zadziała? Pomysł polega na wprowadzeniu pustych danych wejściowych w mapie przełączania, aby obecne obserwowalne było wyłączone, to jest również odrzucane w tym przypadku.

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; 
       } 
      }) 
      .debounceTime(300) 
      .distinctUntilChanged() 
      .switchMap(text=> { 
       if (text.length > 0) { 
       return $.ajax({ 
       type: "post", 
       url: "src/php/search.php", 
       data: { 
        username: text, 
        type: "username" 
       } 
       }).promise()} 
       return Rx.Observable.empty(); 
      } 
     ); 
+0

Spróbuję później i dam ci znać. –

+0

udało się to dobrze w końcu? – user3743222

+0

Przepraszam, nie miałem czasu na sprawdzenie, jest weekend: P –

5

chciałbym zaproponować, aby przenieść rozmowę debounceTime tuż po strumieniem tekstowym i zrobić filtrowanie już na debounced strumienia. W ten sposób ważna, ale nieaktualna wartość nie będzie przekradać się do wywołania ajax. Ponadto, być może będziemy musieli "anulować" wywołanie ajaxowe (lub dokładniej, zignorować jego odpowiedź), gdy pojawi się nowe, prawdopodobnie nieprawidłowe, dane wejściowe, ponieważ nie chcemy, aby status "nieprawidłowy" został zmieniony z wynikiem trwającego asynchronicznego połączenie. takeUntil może w tym pomóc. Zauważ też, że zamiast $.ajax() można użyć Rx.Observable.ajax().

BTW, może być przydatny, aby mieć dedykowany strumień "statusów sprawdzania poprawności", aby this.username_validation_display(validationStatus) było wykonane w jednym miejscu wewnątrz subskrypcji.

sobie wyobrazić, że jest coś takiego:

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"; 
} 

Zachowanie kodu powyżej byłaby również inna w tym sensie, że między punktem w czasie, gdy użytkownik wpisał poprawny wejście i punkt po otrzymaniu sugestii z serwera, username_validation_display będzie miało pustą wartość, tj. oprócz statusów "pusty", "nieprawidłowy", "zajęty" i "prawidłowy" będzie również pusty status pośredni, który może zostać zastąpiony pewnym wskaźnikiem postępu w interfejsie użytkownika.

Aktualizacja: mogę również pomyśleć o alternatywnym podejściu, które powinien posiadać równoważne zachowanie, ale odpowiadający kod może wyglądać nieco bardziej prosta:

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

ten sposób możemy sprawdzić każdego odrębnego wejścia synchronicznie. Jeśli dane wejściowe są puste lub nieprawidłowe (określone przez wywołanie getValidationError()), natychmiast wysyłamy komunikat o błędzie weryfikacji. W przeciwnym razie natychmiast wyślemy status "oczekujący" i uruchomimy timer, który po 300 ms uruchomi sprawdzanie dostępności asynchronicznej nazwy użytkownika. Jeśli nadejdzie nowy tekst przed upływem czasu lub stan dostępności nadejdzie z serwera, anulujemy wewnętrzny strumień i ponownie uruchomimy sprawdzanie poprawności, dzięki switchMap. Powinno działać tak samo jak z debounceTime.

1

Możesz użyć oryginalnych strumieni wartości jako powiadamiającego dla wywołania ajaxowego, aby przerwać jego wartość. Powinno to zapewnić ci wystarczającą kontrolę, aby dodać obsługę dodatkową do wyświetlania komunikatów, jeśli pole jest puste bez nadpisania przez poprzednie wyniki 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>