Trouble inserting multiple text fields into collection


#1

Hello all! I’ve been lurking on these forums for a few months and I really appreciate all the good information.

I’ve been modifying the Meteor1.3+React Todos app to get the basics down and it’s been going well so far. However, i’d like to add another text field so that the user can submit a description of an item (the first field that comes with the Todos app) as well as the cost of that item. I’ve been trying to add the second input field and copy over the values/pass them through to the tasks.js api but I can’t seem to get it to work. I’m aware that this is aesthetically unsettling (hitting enter to input two text fields into a collection) and it may be impossible/is most likely not the correct way to do something like this.

Here’s what I’m working with:

App.jsx

import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { Meteor } from 'meteor/meteor';
import { createContainer } from 'meteor/react-meteor-data';
import { Tasks } from '../api/tasks.js';
import Task from './Task.jsx';
import AccountsUIWrapper from './AccountsUIWrapper.jsx';
// App component - represents the whole app
//render collection of tasks
class App extends Component {
    //componenet contructor that contains initializations for: hideCompleted
    constructor(props) {
        super(props);
        this.state = {
            hideCompleted: false,
        };
    }
    //Event handler for when you press enter to input data.  Calls Meteor method tasks.insert and sends it the text,
    //then clears the text form
    handleSubmit(event) {
        event.preventDefault();
        // Find the task text field via the React ref
        const taskText = ReactDOM.findDOMNode(this.refs.textInput).value.trim();
        // Find the cost field via the React ref
        const costNum = ReactDOM.findDOMNode(this.refs.costInput).value.trim();
        //Call the tasks insert method in tasks.js api
        Meteor.call('tasks.insert', taskText, costNum);
        // Clear task form
        ReactDOM.findDOMNode(this.refs.textInput).value = '';
        //Clear cost form
        ReactDOM.findDOMNode(this.refs.textInput).value = '';
    }
    //Event handler for hideCompleted checkbox check
    toggleHideCompleted() {
        this.setState({
            hideCompleted: !this.state.hideCompleted,
        });
    }
    //Filters out tasks that have hideCompleted === true
    renderTasks() {
        let filteredTasks = this.props.tasks;
        if (this.state.hideCompleted) {
            filteredTasks = filteredTasks.filter(task => !task.checked);
        }
        return filteredTasks.map((task) => (
            <Task key={task._id} task={task} />
        ));
    }
    render() {
        return (
            <div className="container">
                <header>
                    <h1>The Economy</h1> ({this.props.incompleteCount})
                    <label className="hide-completed">
                        <input
                            type="checkbox"
                            readOnly
                            checked={this.state.hideCompleted}
                            onClick={this.toggleHideCompleted.bind(this)}
                        />
                        Hide Completed Tasks
                    </label>
                    <AccountsUIWrapper />
                    { this.props.currentUser ?
                        <form className="new-task" onSubmit={this.handleSubmit.bind(this)} >
                            <input
                                type="text"
                                ref="textInput"
                                placeholder="Type to add new tasks"
                            />
                            <input
                                type="Number"
                                ref="costInput"
                                placeholder="Type to add cost"
                            />
                        </form> : ''
                    }
                </header>
                <ul>
                    {this.renderTasks()}
                </ul>
            </div>
        );
    }
}
//proptypes - set up the tasks proptype
App.propTypes = {
    tasks: PropTypes.array.isRequired,
    incompleteCount: PropTypes.number.isRequired,
    currentUser: PropTypes.object,
};
//exports createContainer function which queries the tasks collection
export default createContainer(() => {
    return {
        tasks: Tasks.find({}, { sort: { createdAt: -1 } }).fetch(),
        incompleteCount: Tasks.find({ checked: { $ne: true } }).count(),
        currentUser: Meteor.user(),
//        currentBalance: Tasks.characters.aggregate([ { $group: { _id: null, total: { $sum: "$cost" } } } ]),
    };
}, App);

tasks.js

import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';
export const Tasks = new Mongo.Collection('tasks');
Meteor.methods({
    'tasks.insert'(text, cost) {
        //Make sure that text is a String
        check(text, String);
        //Make sure that cost is a Number
        check(cost, Number);
        // Make sure the user is logged in before inserting a task
        if (! this.userId) {
            throw new Meteor.Error('not-authorized');
        }
        //Actual database insertion
        Tasks.insert({
            text,
            cost,
            createdAt: new Date(),
            owner: this.userId,
            username: Meteor.users.findOne(this.userId).username,
        });
    },
    'tasks.remove'(taskId) {
        check(taskId, String);
        Tasks.remove(taskId);
    },
    'tasks.setChecked'(taskId, setChecked) {
        check(taskId, String);
        check(setChecked, Boolean);
        Tasks.update(taskId, { $set: { checked: setChecked } });
    },
});

I feel like there’s some type of simple answer out there but after a while of searching, I can’t seem to find anything that makes sense to me. I haven’t dabbled in any React forms packages yet because I thought i’d be able to do this without one. I feel bad about asking about something seemingly so simple but alas here I am. Any recommended reading or methods to look into is greatly appreciated.


#2

I’d recommend adding some simple console.log statements to a few places to help troubleshoot. First add the following to your handleSubmit function, right after you set taskText and costNum (which don’t need to be wrapped in ReactDOM.findDOMNode - you can just call this.refs.costInput.value directly for example):

console.log(taskText);
console.log(costNum);

Then add the same in your tasks.insert Method definition, right after your check calls:

console.log(text);
console.log(cost);

That should help you narrow in on the source of the issue.


#3

Thanks for the heads up on the unnecessary ReactDOM.findDOMNode wrap. Any reason in particular why it isn’t necessary there? It was in taskText originally in the tutorial.

After adding the console.log statements, nothing happens in the console when I fill either of the fields out and press enter. I’m left to assume that it’s something to do with triggering handleSubmit.

Not sure if this is related but this is an error that persists in the console:
WARNING: npm peer requirements (for react-meteor-data) not installed:
_ - react@0.14.8 installed, react@15.x needed_
_ - react-addons-pure-render-mixin@0.14.8 installed, react-addons-pure-render-mixin@15.x needed_

Read more about installing npm peer dependencies:
http://guide.meteor.com/using-packages.html#peer-npm-dependencies