# Meteor Tutorial: Creating Pages

Step 1: Create imports/routes/AllTodos.js

We will now define a page that displays all the Todo items stored in our todos collection.

Create a file imports/routes/AllTodos.js and paste the following code:


/**
 * File: imports/routes/AllTodos.js
 */

import React from 'react';
import { Meteor } from 'meteor/meteor';
import { createContainer } from 'meteor/react-meteor-data';

import {
  Row,
  Col,
  Grid,
  Panel,
  Alert,
  PanelBody,
  PanelContainer,
} from '@sketchpixy/rubix';

import { Todos } from '../api/todos.js';

import Todo from '../components/Todo';
import TodoForm from '../components/TodoForm';

class AllTodos extends React.Component {
  static propTypes = {
    todos: React.PropTypes.array.isRequired,
  };

  render() {
    return (
      <PanelContainer>
        <Panel>
          <PanelBody style={{padding: 0, paddingBottom: 25}}>
            <Grid>
              <Row>
                <Col xs={12}>
                  <h3>Todo List:</h3>

                  <TodoForm />

                  {this.props.todos.map((todo) => {
                    return <Todo key={todo._id} todo={todo} />;
                  })}
                </Col>
              </Row>
            </Grid>
          </PanelBody>
        </Panel>
      </PanelContainer>
    );
  }
}

export default createContainer(() => {
  Meteor.subscribe('todos');

  const todos = Todos.find({}).fetch() || [];

  return {
    todos: todos,
  };
}, AllTodos);

In the above code, we define an AllTodos component that renders all the todo items fetched from the todos collection. We use the createContainer method defined in meteor/react-meteor-data to pre-fill our AllTodos component with the necessary data (in this case the todos prop). We also subscribe to the todos publication name to enable fetching of todo items from the backend.


Step 1: Create imports/routes/EditTodo.js

Now that we have created a page that renders all Todos in our todos collection, let us define a page to edit these todo items.

To do that, create a file imports/routes/EditTodo.js and paste the following code:


import React from 'react';
import ReactDOM from 'react-dom';
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { withRouter } from 'react-router';
import { createContainer } from 'meteor/react-meteor-data';

import {
  Row,
  Col,
  Grid,
  Form,
  Panel,
  Alert,
  Button,
  Checkbox,
  PanelBody,
  FormGroup,
  FormControl,
  ControlLabel,
  PanelContainer } from '@sketchpixy/rubix';

import { Todos } from '../api/todos.js';

@withRouter
class EditTodoForm extends React.Component {
  state = {
    errors: []
  };

  editTodo(e) {
    e.preventDefault();

    let input = ReactDOM.findDOMNode(this.input);
    let todo = input.value;
    let completed = this.checkbox.checked;

    let { _id } = this.props.todo;

    Meteor.call('todos.update', _id, todo, completed, (err, n) => {
      if (err) {
        this.setState({
          errors: [ ].concat(err),
        });
        return;
      }

      this.setState({
        errors: []
      }, () => {
        this.props.router.push('/');
      });
    });
  }

  render() {
    let { todo, completed } = this.props.todo;

    let errors = this.state.errors.length ?
      (
        <Alert danger dismissible>
          {this.state.errors.map(({ message }, i) => {
            return <div key={i}>{message}</div>
          })}
        </Alert>
      ) : null;

    return (
      <div>
        {errors}

        <Form onSubmit={::this.editTodo}>
          <FormGroup controlId='todoText'>
            <ControlLabel>Todo Text</ControlLabel>
            <FormControl type='text' placeholder='A todo item...' defaultValue={todo} ref={(input) => this.input = input} autoFocus />
          </FormGroup>
          <FormGroup controlId='todoComplete'>
            <Checkbox inputRef={(checkbox) => { this.checkbox = checkbox; }} defaultChecked={completed} >
              Mark as Completed
            </Checkbox>
          </FormGroup>
          <FormGroup>
            <Button type='submit' bsStyle='blue' onlyOnHover>Update Todo</Button>
          </FormGroup>
        </Form>
      </div>
    );
  }
}


class EditTodo extends React.Component {
  static propTypes = {
    todo: React.PropTypes.object,
  };

  render() {
    let { todo } = this.props;

    if (!todo) return null;

    return (
      <PanelContainer>
        <Panel>
          <PanelBody style={{padding: 0, paddingBottom: 25}}>
            <Grid>
              <Row>
                <Col xs={12}>
                  <h3>Editing Todo Item:</h3>

                  <EditTodoForm todo={todo} />
                </Col>
              </Row>
            </Grid>
          </PanelBody>
        </Panel>
      </PanelContainer>
    );
  }
}

export default createContainer(({ params }) => {
  let { id } = params;
  let _id = id;
  Meteor.subscribe('todo', _id);

  return {
    todo: Todos.find({ _id }).fetch()[0],
  };
}, EditTodo);

The above code, like in the AllTodos.js file, implements a EditTodo component that in turn renders an EditTodoForm component. The route for EditTodo has an id parameter that is available when the user navigates to /todo/edit/:id (we will be defining these routes in the next section). This id parameter corresponds to the _id parameter of Todo items stored in the todos collection. In the createContainer method, we retrive the current todo id from the params variable supplied to the component by ReactRouter and then subscribe to the todo publication name passing along the _id.

Once subscribed, the Todo item is fetched for EditTodo to consume.

EditTodo component, on the other hand, transfers the todo data to EditTodoForm component. The EditTodoForm component, in turn, renders a form with the todo data pre-filled. When the form is submitted, the editTodo method is called. The method then retrives the form data and invokes Meteor.call('todos.update', _id, todo, completed) passing along the form data into the RPC. If there are no errors, the router is instructed to navigate the page back to the homepage.