ucesco
ucesco

Reputation: 33

Cycle.js getting input value on button click

I'm working on todo list sample app using Cycle.js and I came across following problem. I'm trying handle add new todo item when user clicks Add button, which works fine, but on the other hand I have a stream which provides text input change events and when combined with the click stream it makes my app to add new todo items even in case when the text input is changed, but the Add button is not clicked e.g. focus out. What is the approach in such cases? Can I just eliminate todoChange$ stream and access DOM from addClick$ stream directly, or it would be against Cycle.js philosophy?

JS Bin: https://jsbin.com/wugawaheni/edit?js,console,output

const xs = xstream.default;
const {div, input, p, makeDOMDriver} = CycleDOM;

const intent = (DOMSource) => {
  const addClick$ = DOMSource.select('.add').events('click').map(ev => true);
  const todoChange$ = DOMSource.select('.todo').events('change').map(ev => ev.target.value);

  return { addClick$, todoChange$ };
};

const model = (addClick$, todoChange$) => {
  const add$ = addClick$.startWith(false);
  const todo$ = todoChange$.startWith('');

  return xs.combine(add$, todo$)
    .map((combined$) => combined$[1])
    .fold((todos, todo) => {
      todo.trim() && todos.push(todo);
      return todos;
    }, []);
};

const view = state$ => state$.map(todos => div([
  input({attrs: {type: 'text', class: 'todo'}}),
  input({attrs: {type: 'submit', value: 'Add', class: 'add'}}),
  div(todos.map(todo => p(todo)))
]));


const main = (sources) => {
  const { addClick$, todoChange$ } = intent(sources.DOM);
  const state$ = model(addClick$, todoChange$);
  const vdom$ = view(state$);

   return {
     DOM: vdom$
   };
 };

Cycle.run(main, {
   DOM: makeDOMDriver('#app')
 });

Upvotes: 1

Views: 357

Answers (1)

bloodyKnuckles
bloodyKnuckles

Reputation: 12089

Include xstream/extra/sampleCombine, then...

Change:

//return xs.combine(add$, todo$)
return add$.compose(sampleCombine(todo$))

From sampleCombine docs:

Marble diagram:

--1----2-----3--------4--- (source)
----a-----b-----c--d------ (other)
     sampleCombine

-------2a----3b-------4d--

In this case the add$ is source, and todo$ is other. The resulting stream only emits when other has emitted a value, AND upon a value emitted by source. In other words, the stream only emits when source emits if a value is available on other to combine with.

NOTE: sampleCombine is not included in the default xstream library.

ESNextbin demo.

Upvotes: 3

Related Questions