Skip to content
This repository has been archived by the owner on Mar 26, 2019. It is now read-only.

Latest commit

 

History

History
213 lines (174 loc) · 5.63 KB

TUTORIAL.md

File metadata and controls

213 lines (174 loc) · 5.63 KB

We're going to walk through the creation of a todo app using Reaxpress from start to finish.

git clone [email protected]:austingray/reaxpress.git reaxpress-todo-app
cd reaxpress-todo-app
npm install

Let's create a database migration file with Knex

knex --knexfile=.knex/knexfile.js migrate:make todo

Edit the newly created migration file:

exports.up = (knex, Promise) => {
  return Promise.all([
    knex.schema.createTable('todos', (table) => {
      table.increments().primary();
      table.timestamps(true, true);
      table.string('title');
      table.string('description');
      table.boolean('complete');
    }),
  ]);
};

exports.down = (knex, Promise) => {
  return Promise.all([
    knex.schema.dropTable('todos'),
  ]);
};

Then run the migration:

knex --knexfile=.knex/knexfile.js migrate:latest

Now let's create a database model file using the CLI:

./reaxpress model todos

This will generate a model file that extends our base model class and provides the following methods for the table todos:

create
fetch
fetchMany
update
delete

Check out the file models/_model.jsx to see expected arguments and return values (and/or check back for updated documentation).

Great! Now we have a fully functioning database model. Let's create some endpoints:

./reaxpress.js route todos
./reaxpress.js route todos/add

This created the following files:

routes/todos.jsx
src/react/App/Todos/index.jsx
src/react/App/TodosAdd/index.jsx

routes/todos.jsx is where we will handle our back end logic while the 2 new files in the react directories are the corresponding front end views for each endpoint.

Let's add a form for creating new todos. Open src/react/TodosAdd.

Replace:

<Content>
  TodosAdd content
</Content>

With:

<Content>
  <h1>Add a Todo</h1>
  <form method="post">
    <div className="form-group">
      <label htmlFor="todo-title">Title</label>
      <input className="form-control" type="text" name="title" id="todo-title" />
    </div>
    <div className="form-group">
      <label htmlFor="todo-description">Description</label>
      <textarea className="form-control" name="description" id="todo-description" />
    </div>
    <button type="submit" className="btn btn-primary">Submit</button>
  </form>
</Content>

Now we need to handle the form on the backend. All non-get requests need to be manually created. Open routes/todos.jsx. At the top of the file after our module imports, let's import our database models.

import Models from '../models';

and add the following at the very end, after // end of #reaxpress routes and before module.exports = router;:

router.post('/add', async (req, res) => {
  await new Models.Todos().create(req.body);
  res.redirect('/todos');
});

Navigate your browser to http://localhost:3000/todos/add and add a few todos.

All done? Great! Now would be a good time to display them on our todos page. While still in routes/todos.jsx, edit the top level request which begins with router.get('/', ... and add the following:

const todos = new Models.Todos();
reaxpressData.todos = await todos.fetchMany();

that route should look like this now:

router.get('/', async (req, res) => {
  const reaxpressData = res.locals.reaxpressData;
  reaxpressData.todos = await new Models.Todos().fetchMany();
  reaxpressResponseHandler(req, res, Todos, reaxpressData);
});

While we're at it, let's drop in another POST route to the bottom of the file after our previous /add post endpoint. This next one will handle updates, which we will use to mark our todos as completed:

router.post('/update/:id', async (req, res) => {
  await new Models.Todos().update(req.body);
  res.sendStatus(204);
});

Now open the file src/react/Todos and update the component to be the following:

@Reaxpress
class Todos extends React.Component {
  completeTodo(todo) {
    // create our new todo
    const updatedTodo = Object.assign({}, todo, {
      complete: !todo.complete,
    });

    // update our application on the front end
    window.reaxpress.update({
      todos: this.props.reaxpressData.todos.map((oldTodo) => {
        if (oldTodo.id === todo.id) {
          return updatedTodo;
        }
        return oldTodo;
      }),
    });

    // send it to the server to be updated
    $.ajax({
      url: `/todos/update/${todo.id}`,
      method: 'POST',
      data: updatedTodo,
      success() {},
    });
  }
  render() {
    const todos = this.props.reaxpressData.todos;
    return (
      <div>
        <Header />
        <Content>
          <h1>Your Todo List</h1>
          <div className="list-group">
            {
              todos.map(todo =>
                <a
                  key={todo.id}
                  onClick={() => { this.completeTodo(todo); }}
                  className="list-group-item list-group-item-action flex-column align-items-start"
                  style={{ opacity: (todo.complete ? 0.2 : 1) }}
                >
                  <div className="d-flex w-100 justify-content-between">
                    <h5 className="mb-1">{todo.title}</h5>
                    <small>{todo.created_at.toString()}</small>
                  </div>
                  <p className="mb-1">{todo.description}</p>
                </a>,
              )
            }
          </div>
          <a href="/todos/add">Add a New Todo</a>
        </Content>
        <Footer />
      </div>
    );
  }
}

This might seem like a lot but break down the above code to see how Reaxpress is handling application state. More detailed tutorial breakdown coming soon.