react javascript rxjs

javascript - react - rxjs pipe



rxjs-Desechar futuros observables retornables (3)

Así que aquí está mi código observable:

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

El problema que tengo es que cuando la entrada está vacía, tengo otra pieza de código que básicamente devuelve un ''error: entrada vacía'', pero el observable que regresa la anula. Así que me preguntaba si habría una manera de ignorar todos los observables si el text.length es 0, pero también volver a suscribirse cuando la longitud del texto no sea cero.

He pensado en unsubscribe pero no sé dónde encajarlo para intentarlo.


Me gustaría proponer mover la llamada debounceTime justo después de la secuencia de entrada de texto y hacer el filtrado ya en la secuencia de la cuenta. De esta manera, un valor válido pero desactualizado no se colará en una llamada ajax. Además, es posible que tengamos que "cancelar" una llamada ajax (o, más precisamente, ignorar su respuesta) cuando se produce una entrada nueva, posiblemente no válida, ya que no queremos que el estado "inválido" se anule con el resultado de un proceso asíncrono continuo. llamada. takeUntil puede ayudar con eso. Tenga en cuenta también que Rx.Observable.ajax() se puede usar en lugar de $.ajax() .

Por cierto, podría ser útil tener un flujo dedicado de "estados de validación" para que this.username_validation_display(validationStatus) se realice en un solo lugar dentro de suscribirse.

Me imagino que es algo como esto:

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

El comportamiento del código anterior también sería diferente en el sentido de que entre un punto en el tiempo cuando un usuario ha escrito una entrada válida y un punto cuando ha llegado una sugerencia del servidor, username_validation_display tendrá un valor vacío, es decir, además de "vacío Los estados "," inválido "," tomado "y" válido "también tendrían un estado vacío intermedio que podría reemplazarse con alguna indicación de progreso en la interfaz de usuario.

Actualización: también puedo pensar en un enfoque alternativo que debería tener un comportamiento equivalente, pero el código correspondiente podría parecer un poco más sencillo:

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

De esta manera comprobamos cada entrada distinta de forma síncrona. Si la entrada está vacía o no es válida (determinada por la llamada getValidationError() ), emitimos el error de validación de inmediato. De lo contrario, emitimos inmediatamente un estado "pendiente" e iniciamos un temporizador que generará una verificación de disponibilidad de nombre de usuario asíncrono después de 300 ms. Si llega un nuevo texto antes de que haya transcurrido el tiempo del temporizador o haya llegado el estado de disponibilidad del servidor, cancelamos la transmisión interna y reiniciamos la validación, gracias a switchMap . Entonces debería funcionar más o menos igual que con debounceTime .


Puede usar sus flujos de valores originales como notificador para que su llamada ajax aborte su valor. Esto debería darle suficiente control para agregar un manejo adicional para mostrar mensajes si el campo está vacío sin que se haya sobrescrito con los resultados anteriores de 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>


Tal vez esto podría funcionar? La idea es incorporar una entrada vacía en el mapa de interruptores para que el observable actual se apague, es decir, se descarte también en ese caso.

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