过滤React Js中的Todo列表

时间:2016-06-20 19:13:27

标签: javascript jquery reactjs frontend

我是React的新手(我以前常常使用Angular),我目前正在根据类别选择过滤我的Todo列表应用。

我从http://todomvc.com/examples/react/#/克隆了Todo列表应用程序。我添加了一个'类别'输入,有效,但现在我试图在列表显示后按类别过滤。

我目前没有任何类别的搜索功能,我正在寻找一些指导,从哪里开始。我将发布以下代码,但如果您想要克隆它,请点击此链接:https://github.com/aenser/todo-react

app.jsx

    var app = app || {};

(function () {
    'use strict';

    app.ALL_TODOS = 'all';
    app.ACTIVE_TODOS = 'active';
    app.COMPLETED_TODOS = 'completed';
    var TodoFooter = app.TodoFooter;
    var TodoItem = app.TodoItem;

    var ENTER_KEY = 13;

    var TodoApp = React.createClass({
        getInitialState: function () {
            return {
                nowShowing: app.ALL_TODOS,
                editing: null,
                newTodo: '',
                newCategory: ''
            };
        },

        componentDidMount: function () {
            var setState = this.setState;
            var router = Router({
                '/': setState.bind(this, {nowShowing: app.ALL_TODOS}),
                '/active': setState.bind(this, {nowShowing: app.ACTIVE_TODOS}),
                '/completed': setState.bind(this, {nowShowing: app.COMPLETED_TODOS})
            });
            router.init('/');
        },

        handleChange: function (event) {
            this.setState({newTodo: event.target.value});
        },

        handleCategoryChange: function (event) {
            this.setState({newCategory: event.target.value});
        },

        handleNewTodoKeyDown: function (event) {
            if (event.keyCode !== ENTER_KEY) {
                return;
            }

            event.preventDefault();

            var val = this.state.newTodo.trim();
            var cat = this.state.newCategory.trim();

            if (val, cat) {
                this.props.model.addTodo(val, cat);
                this.setState({newTodo: '', newCategory: ''});
            }
        },

        toggleAll: function (event) {
            var checked = event.target.checked;
            this.props.model.toggleAll(checked);
        },

        toggle: function (todoToToggle) {
            this.props.model.toggle(todoToToggle);
        },

        destroy: function (todo) {
            this.props.model.destroy(todo);
        },

        edit: function (todo) {
            this.setState({editing: todo.id});
        },

        save: function (todoToSave, text, cat) {
            this.props.model.save(todoToSave, text, cat);
            this.setState({editing: null});
        },

        cancel: function () {
            this.setState({editing: null});
        },

        clearCompleted: function () {
            this.props.model.clearCompleted();
        },

        render: function () {
            var footer;
            var main;
            var todos = this.props.model.todos;

            var shownTodos = todos.filter(function (todo) {
                switch (this.state.nowShowing) {
                case app.ACTIVE_TODOS:
                    return !todo.completed;
                case app.COMPLETED_TODOS:
                    return todo.completed;
                default:
                    return true;
                }
            }, this);

            var todoItems = shownTodos.map(function (todo) {
                return (
                    <TodoItem
                        key={todo.id}
                        todo={todo}
                        onToggle={this.toggle.bind(this, todo)}
                        onDestroy={this.destroy.bind(this, todo)}
                        onEdit={this.edit.bind(this, todo)}
                        editing={this.state.editing === todo.id}
                        onSave={this.save.bind(this, todo)}
                        onCancel={this.cancel}
                    />
                );
            }, this);

            var activeTodoCount = todos.reduce(function (accum, todo) {
                return todo.completed ? accum : accum + 1;
            }, 0);

            var completedCount = todos.length - activeTodoCount;

            if (activeTodoCount || completedCount) {
                footer =
                    <TodoFooter
                        count={activeTodoCount}
                        completedCount={completedCount}
                        nowShowing={this.state.nowShowing}
                        onClearCompleted={this.clearCompleted}
                    />;
            }

            if (todos.length) {
                main = (
                    <section className="main">
                        <input
                            className="toggle-all"
                            type="checkbox"
                            onChange={this.toggleAll}
                            checked={activeTodoCount === 0}
                        />
                        <ul className="todo-list">
                            {todoItems}
                        </ul>
                    </section>
                );
            }

            return (
                <div>
                    <header className="header">
                        <h1>todos</h1>
                        <form onKeyDown={this.handleNewTodoKeyDown}>
                            <input
                                placeholder="What needs to be done?"
                                value={this.state.newTodo}
                                autoFocus={true}
                                className="new-todo"
                                onChange={this.handleChange}
                            />
                        <select value={this.state.newCategory} className="new-todo"
                        onChange={this.handleCategoryChange}>
                            <option value="">Select a Category</option>
                            <option value="Urgent">Urgent</option>
                            <option value="Soon">Soon</option>
                            <option value="Anytime">Anytime</option>
                        </select>

                        </form>
                    </header>
                    {main}
                    {footer}
                </div>
            );
        }
    });

    var model = new app.TodoModel('react-todos');

    function render() {
        React.render(
            <TodoApp model={model}/>,
            document.getElementsByClassName('todoapp')[0]
        );
    }

    model.subscribe(render);
    render();
})();

todoModel.js

var app = app || {};

(function () {
    'use strict';

    var Utils = app.Utils;
    // Generic "model" object. You can use whatever
    // framework you want. For this application it
    // may not even be worth separating this logic
    // out, but we do this to demonstrate one way to
    // separate out parts of your application.
    app.TodoModel = function (key) {
        this.key = key;
        this.todos = Utils.store(key);
        this.onChanges = [];
    };

    app.TodoModel.prototype.subscribe = function (onChange) {
        this.onChanges.push(onChange);
    };

    app.TodoModel.prototype.inform = function () {
        Utils.store(this.key, this.todos);
        this.onChanges.forEach(function (cb) { cb(); });
    };

    app.TodoModel.prototype.addTodo = function (title, category) {
        this.todos = this.todos.concat({
            id: Utils.uuid(),
            title: title,
            category: category,
            completed: false
        });

        this.inform();
    };

    app.TodoModel.prototype.toggleAll = function (checked) {
        // Note: it's usually better to use immutable data structures since they're
        // easier to reason about and React works very well with them. That's why
        // we use map() and filter() everywhere instead of mutating the array or
        // todo items themselves.
        this.todos = this.todos.map(function (todo) {
            return Utils.extend({}, todo, {completed: checked});
        });

        this.inform();
    };

    app.TodoModel.prototype.filterAll = function () {
        this.todos = this.todos.map(function (todo) {
            return Utils.extend({}, todo);
        });

        this.inform();
    };

    app.TodoModel.prototype.toggle = function (todoToToggle) {
        this.todos = this.todos.map(function (todo) {
            return todo !== todoToToggle ?
                todo :
                Utils.extend({}, todo, {completed: !todo.completed});
        });

        this.inform();
    };

    app.TodoModel.prototype.destroy = function (todo) {
        this.todos = this.todos.filter(function (candidate) {
            return candidate !== todo;
        });

        this.inform();
    };

    app.TodoModel.prototype.save = function (todoToSave, text, cat) {
        this.todos = this.todos.map(function (todo) {
            return todo !== todoToSave ? todo : Utils.extend({}, todo, {title: text}, {category: cat});
        });

        this.inform();
    };

    app.TodoModel.prototype.clearCompleted = function () {
        this.todos = this.todos.filter(function (todo) {
            return !todo.completed;
        });

        this.inform();
    };

})();

todoItem.jsx

var app = app || {};

(function () {
    'use strict';

    var ESCAPE_KEY = 27;
    var ENTER_KEY = 13;

    app.TodoItem = React.createClass({
        handleSubmit: function (event) {
            var val = this.state.editText.trim();
            var cat = this.state.editCategoryText.trim();
            if (val || cat) {
                this.props.onSave(val, cat);
                this.setState({editText: this.props.todo.title, editCategoryText: this.props.todo.category});
            } else {
                this.props.onDestroy();
            }
        },

        handleEdit: function (event) {
            this.props.onEdit();
            this.setState({editText: this.props.todo.title, editCategoryText: this.props.todo.category});
        },

        handleKeyDown: function (event) {
            if (event.which === ESCAPE_KEY) {
                this.setState({editText: this.props.todo.title});
                this.props.onCancel(event);
            } else if (event.which === ENTER_KEY) {
                this.handleSubmit(event);
            }
        },

        handleChange: function (event) {
            if (this.props.editing) {
                this.setState({editText: event.target.value});
            }
        },

        handleCategoryChange: function (event) {
            if (this.props.editing) {
                this.setState({editCategoryText: event.target.value});
            }
        },

        getInitialState: function () {
            return {editText: this.props.todo.title, editCategoryText: this.props.todo.category};
        },

        /**
         * This is a completely optional performance enhancement that you can
         * implement on any React component. If you were to delete this method
         * the app would still work correctly (and still be very performant!), we
         * just use it as an example of how little code it takes to get an order
         * of magnitude performance improvement.
         */
        shouldComponentUpdate: function (nextProps, nextState) {
            return (
                nextProps.todo !== this.props.todo ||
                nextProps.editing !== this.props.editing ||
                nextState.editText !== this.state.editText ||
                nextState.editCategoryText !== this.state.editCategoryText
            );
        },

        /**
         * Safely manipulate the DOM after updating the state when invoking
         * `this.props.onEdit()` in the `handleEdit` method above.
         * For more info refer to notes at https://facebook.github.io/react/docs/component-api.html#setstate
         * and https://facebook.github.io/react/docs/component-specs.html#updating-componentdidupdate
         */
        componentDidUpdate: function (prevProps) {
            if (!prevProps.editing && this.props.editing) {
                var node = React.findDOMNode(this.refs.editField);
                node.focus();
                node.setSelectionRange(node.value.length, node.value.length);
            }
        },

        render: function () {
            return (
                <li className={classNames({
                    completed: this.props.todo.completed,
                    editing: this.props.editing
                })}>
                    <div className="view">
                        <input
                            className="toggle"
                            type="checkbox"
                            checked={this.props.todo.completed}
                            onChange={this.props.onToggle}
                        />
                        <label onDoubleClick={this.handleEdit}>
                            {this.props.todo.title}
                        </label>
                        <label onDoubleClick={this.handleEdit}>
                            {this.props.todo.category}
                        </label>
                        <button className="destroy" onClick={this.props.onDestroy} />
                    </div>
                        <input
                            ref="editField"
                            value={this.state.editText}
                            className="edit"
                            onChange={this.handleChange}
                            onKeyDown={this.handleKeyDown}
                        />
                        <select value={this.state.EditCategoryText} className="edit" onChange={this.handleCategoryChange} defaultValue={this.props.todo.category} onKeyDown={this.handleKeyDown}>
                            <option value="Urgent">Urgent</option>
                            <option value="Soon">Soon</option>
                            <option value="Anytime">Anytime</option>
                        </select>
                </li>
            );
        }
    });
})();

感谢您抽出宝贵时间帮助我了解如何根据类别选择过滤搜索。

1 个答案:

答案 0 :(得分:1)

你的界面有点令人困惑,因为你似乎使用相同的输入选择为todos和过滤分配类别,我会在答案的最后得到它,但是现在,我只是使用了用于输入数据和按类别过滤的类别选择器。

你的问题的答案非常简单。您只需按类别和完成状态进行过滤。像这样:

            var shownTodos = todos.filter(function(todo) {
                return(todo.category === this.state.newCategory);
            }, this).filter(function (todo) {
                    switch (this.state.nowShowing) {
                    case app.ACTIVE_TODOS:
                            return !todo.completed;
                    case app.COMPLETED_TODOS:
                            return todo.completed;
                    default:
                            return true;
                    }
            }, this);

我会在底部为当前显示的分类添加更多按钮。您还可以为nowShowing等类别添加一组新的状态,例如nowShowingCategory。按钮会将其设置为类别的3个值,您可以在上面的过滤器中使用该变量,而不是我的示例中的newCategory

相关问题