How do I focus an input with Cycle.js and RxJS?
Asked Answered
C

3

5

How do I focus an input with Cycle? Do I need to reach inside the DOM and call .focus() either with or without jQuery, or is there some other way with Cycle/RxJS?

Conformable answered 28/1, 2016 at 1:21 Comment(0)
M
6

Yes, you do need to reach inside the DOM and call .focus() either with or without jQuery. However this is a side-effect and it is Cycle.js convention to move these kinds of side effects to a so-called driver.

The two questions the driver needs to know are:

  • which element do you want to focus?
  • when do you want to focus the element?

The answer to both questions can be provided by a single stream of DOM elements.

Create the driver

First make your driver. Let's call it SetFocus. We'll make it a so-called read-only driver. It will read from the app's sinks but it will not provide a source to the app. Because it is reading, the driver's function will need to accept a formal parameter that will be a stream, call it elem$:

function makeSetFocusDriver() {
  function SetFocusDriver(elem$) {
    elem$.subscribe(elem => {
      elem.focus();
    });
  }
  return SetFocusDriver;
}

This driver takes whatever DOM element arrives in the stream and calls .focus() on it.

Use the Driver

Add it to the list of drivers provided to the Cycle.run function:

Cycle.run(main, {
  DOM: makeDOMDriver('#app'),
  SetFocus: makeSetFocusDriver() // add a driver
});

Then in your main function:

function main({DOM}) {

  // setup some code to produce the elem$ stream
  // that will be read by the driver ...
  // [1]: say _when_ we want to focus, perhaps we need to focus when
  //      the user clicked somewhere, or maybe when some model value
  //      has changed
  // [2]: say _what_ we want to focus
  //      provide the textbox dom element as actual value to the stream
  //      the result is:
  //      |----o-----o-----o--->
  //      where each o indicates we want to focus the textfield
  //      with the class 'field'
  const textbox$ = DOM.select('.field').observable.flatMap(x => x); // [2]
  const focusNeeded = [
    clickingSomewhere$,    // [1]
    someKindofStateChange$ // [1]
  ];
  const focus$ = Observable.merge(...focusNeeded)
    .withLatestFrom(textbox$, (_, textbox) => textbox); // [2]

  // ...

  // [*]: Add driver to sinks, the driver reads from sinks.
  //      Cycle.js will call your driver function with the parameter
  //      `elem$` being supplied with the argument of `focus$`
  return {
    DOM: vtree$,
    SetFocus: focus$, // [*]
  };
}

You can then configure focusNeeded to say when you want .field to be focused.

Mahaliamahan answered 25/3, 2016 at 11:4 Comment(2)
Good answer. Minor grammatical error (two/to), which I'd have edited except that edits have to be at least six characters.Narcissus
Thanks @MattBurnell - I've made the edit to fix the error ("two" replaced with "to").Mahaliamahan
K
5

You can tailor for your own situation, but this should illustrate how to solve your problem. Let's assume you have a text input and a button. When the button is clicked, you want the focus to remain on the text input.

First write the intent() function:

function intent(DOMSource) {
    const textStream$ = DOMSource.select('#input-msg').events('keyup').map(e => e.target);
    const buttonClick$ = DOMSource.select('#send-btn').events('click').map(e => e.target);

    return buttonClick$.withLatestFrom(textStream$, (buttonClick, textStream) => {
        return textStream;
    });
}

Then the main which has a sink to handle the lost focus side effect

function main(sources) {
    const textStream$ = intent(sources.DOM);

    const sink = {
       DOM: view(sources.DOM),
       EffectLostFocus: textStream$,
    }

    return sink;
}

Then the driver to handle this side effect would look something like

Cycle.run(main, {
    DOM: makeDOMDriver('#app'),
    EffectLostFocus: function(textStream$) {    
         textStream$.subscribe((textStream) => {
         console.log(textStream.value);
         textStream.focus();
         textStream.value = '';
      })
    }
});

The entire example is in this codepen.

Kennykeno answered 3/2, 2016 at 21:16 Comment(0)
C
2

Here's one example, written by Mr. Staltz himself: https://github.com/cyclejs/cycle-examples/blob/master/autocomplete-search/src/main.js#L298

Conformable answered 28/1, 2016 at 8:0 Comment(1)
Yeah, don't be afraid to write your own small drivers to handling things. They're really very simple functions where you isolate effects.Bis

© 2022 - 2024 — McMap. All rights reserved.