Cyclejs input not resetting after redraw

101 Views Asked by At

I tried to create a simple todo app with Cyclejs/xstream. The app works fine. Only thing I not able to understand is after adding each todo the input should clear, which is not happening.

todo.js

import {
    div, span, p, input, ul, li, button, body
}
from '@cycle/dom'
import xs from 'xstream'
import Utils from './utils'

export function Todo(sources) {
    const sinks = {
        DOM: view(model(intent(sources)))
    }
    return sinks
}

function intent(sources) {
    return {
        addTodo$: sources.DOM.select('input[type=text]').events('keydown').filter((ev) => {
            return ev.which == 13 && ev.target.value.trim().length > 0;
        }).map((ev) => {
            return ev.target.value;
        }),
        deleteTodo$: sources.DOM.select('.delete').events('click').map((ev) => {
            return Number(ev.target.getAttribute('data-id'));
        }).filter((id) => {
            return !isNaN(id);
        }),
        completeTodo$: sources.DOM.select('.complete').events('click').map((ev) => {
            return Number(ev.target.getAttribute('data-id'));
        }).filter((id) => {
            return !isNaN(id);
        })
    };
}

function model(action$) {
    let deleteTodo$ = action$.deleteTodo$.map((id) => {
        return (holder) => {
            let index = Utils.findIndex(holder.currentTodos, 'id', id);
            if (index > -1) holder.currentTodos.splice(index, 1);
            return {
                currentTodos: holder.currentTodos,
                value: ''
            };
        };
    });
    let completeTodo$ = action$.completeTodo$.map((id) => {
        return (holder) => {
            let index = Utils.findIndex(holder.currentTodos, 'id', id);
            if (index > -1) holder.currentTodos[index].completed = !holder.currentTodos[index].completed;
            return {
                currentTodos: holder.currentTodos,
                value: ''
            };
        };
    });
    let addTodo$ = action$.addTodo$.map((item) => {
        return (holder) => {
            let todo = {
                value: item,
                id: holder.currentTodos.length + 1,
                completed: false
            };
            holder.currentTodos.push(todo);
            return {
                currentTodos: holder.currentTodos,
                value: ''
            };
        };
    });
    return xs.merge(deleteTodo$, addTodo$, completeTodo$)
        .fold((holder, modifier) => {
            return modifier(holder);
        }, {
            currentTodos: [],
            value: ''
        });
}

function view(state$) {
    return state$.map((state) => {
        console.log(state);
        return div({
            attrs: {
                class: 'todo'
            }
        }, [
            input({
                props: {
                    type: 'text',
                    value: state.value
                }
            }),
            ul({
                attrs: {
                    class: 'text'
                }
            }, state.currentTodos.map((todo) => {
                return li({
                    attrs: {
                        class: `${todo.completed ? 'completed' : 'open'}`
                    }
                }, [
                    span(todo.value),
                    button({
                        attrs: {
                            class: 'delete',
                            'data-id': todo.id

                        }
                    }, 'XXXXX'),
                    button({
                        attrs: {
                            class: 'complete',
                            'data-id': todo.id

                        }
                    }, 'CCCCC')
                ]);
            }))
        ]);
    });
}

utils.js

var Utils = {
    filter: function(array, fn) {
        var results = [];
        var item;
        for (var i = 0, len = array.length; i < len; i++) {
            item = array[i];
            if (fn(item)) results.push(item);
        }
        return results;
    },
    findItem: function(array, fn) {
        for (var i = 0, len = array.length; i < len; i++) {
            var item = array[i];
            if (fn(item)) return item;
        }
        return null;
    },
    findIndex: function(array, prop, value) {
        var pointerId = -1;
        var index = -1;
        var top = array.length;
        var bottom = 0;
        for (var i = array.length - 1; i >= 0; i--) {
            index = bottom + (top - bottom >> 1);
            pointerId = array[index][prop];
            if (pointerId === value) {
                return index;
            } else if (pointerId < value) {
                bottom = index;
            } else if (pointerId > value) {
                top = index;
            }
        }
        return -1;
    }
};

export default Utils;
1

There are 1 best solutions below

2
Nurlan Mirzayev On BEST ANSWER

You need to put hook inside input element. It will work as expected. If you want you can send another default value (in this case it is empty string).

input({
     props: {
          type: 'text'
     },
     hook: {
          update: (o, n) => n.elm.value = ''
     }
 }),

@cycle/dom driver should be > 11.0.0 which works with Snabbdom. But if you use earlier version you need:

var Hook = function(){
    this.arguments=arguments;
}
Hook.prototype.hook = function(node) {
    node.value=this.arguments[0];
}


input({ attributes: {type: 'text'},
      'my-hook':new Hook('')
})