# Laravel Tutorial: Creating a basic UI

Step 1: Create laravel-todolist/src/components/Todo.js

Let us create our first React component: Todo. Paste the following code into the newly create Todo.js file:


import React from 'react';
import { withRouter } from 'react-router';

import {
  Col,
  Row,
  Grid,
  Icon,
  Button,
  Checkbox,
  ButtonGroup,
} from '@sketchpixy/rubix';

import client from '@sketchpixy/rubix/lib/utils/HttpClient';

@withRouter
export default class Todo extends React.Component {
  constructor(props) {
    super(props);

    let { todo } = props;

    this.state = {
      deleted: false,
      todo: todo.todo,
      completed: parseInt(todo.completed),
    };
  }

  toggleCompletion() {
    let completed = this.input.checked ? 1 : 0;
    client.put(`/api/todos/${this.props.todo.id}`, {
      completed,
    }).then((result) => {
      this.setState({
        todo: result.data.todo.todo,
        completed: parseInt(result.data.todo.completed),
      });
    });
  }

  removeTodo() {
    client.delete(`/api/todos/${this.props.todo.id}`)
          .then(() => {
             this.setState({ deleted: true });
          });
  }

  editTodo() {
    this.props.router.push(`/todos/${this.props.todo.id}`);
  }

  render() {
    if (this.state.deleted) return null;

    let { todo, completed } = this.state;
    let style = {
      textDecoration: completed ? 'line-through' : null
    };

    return (
      <Grid>
        <Row className='todo-item'>
          <Col sm={8}>
            <Checkbox onChange={::this.toggleCompletion} style={style} inputRef={(input) => { this.input = input; }} checked={completed} >
              {todo}
            </Checkbox>
          </Col>
          <Col sm={4} className='text-right'>
            <Button bsStyle='red' className='remove-sm' onClick={::this.removeTodo} style={{marginRight: 12.5}}>Remove</Button>
            <Button bsStyle='green' className='remove-sm' onlyOnHover onClick={::this.editTodo}>Edit</Button>
          </Col>
        </Row>
      </Grid>
    );
  }
}

Let's breakdown the above code:

  • The above code defines a simple Todo component class. This component will be used in the index page where we render all todo items stored in our database.

  • We import all the necessary basic UI components from "@sketchpixy/rubix" for building our Todo component. To see all the different components that come bundled with Rubix please refer to the COMPONENTS section in the sidebar for complete documentation.

  • In Line-14, we import the HttpClient module which we will use in making GET, POST, PUT and DELETE requests to our Laravel backend. Why can't we just make use of jQuery here? The HttpClient module that we provide is a Promise based library which makes it easier to write shorter and simpler code. The library also takes care of handling adding CSRF tokens to the request internally so you don't have to worry about manually appending the CSRF token to your request. In the future sections, you'll see how much easier it is to use the HttpClient module as opposed to executing AJAX requests via jQuery.

  • Lines 23 - 27 set the initial state of the Todo component. The state is set to the todo prop received from the parent component.

  • Lines 30 - 40 define a toggleCompletion() instance method that sets the completion state of the Todo item that was clicked. Since we are modifying the todo item, we make a client.put request to the endpoint /api/todos/${this.props.todo.id}. If you recall the dingo API routes we defined in the previous section, /api/todos/{id} URI corresponds to a PUT method. The result is fetched in the then callback and the new state is set.

  • Lines 42 - 47 define a removeTodo() instance method. A call to this method removes the todo item. Since we are deleting the todo item, we make a client.delete request to the endpoint /api/todos/${this.props.todo.id}. Again, recall the dingo API route defined in the previous section had a /api/todos/{id} corresponding to a DELETE method. Once the deletion is successful we set the deleted state to true, causing the component to render null.

  • Line 49 - 51 defines a editTodo() instance method. When this method is called, the router navigates to the edit page where the todo can be edited. We will cover editing in the upcoming section.

  • The render method is pretty self-explanatory in that it defines a simple Checkbox that displays the todo item text and two buttons which call removeTodo and editTodo which clicked.

Step 2: Create laravel-todolist/src/components/TodoForm.js

Let us now define a React component called TodoForm which handles creating new Todo items. Paste the following code into the newly create TodoForm.js file:


import React from 'react';
import ReactDOM from 'react-dom';

import client from '@sketchpixy/rubix/lib/utils/HttpClient';

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

export default class TodoForm extends React.Component {
  state = {
    errors: {},
  };

  createTodo(e) {
    e.preventDefault();

    let input = ReactDOM.findDOMNode(this.input);

    let todo = input.value;

    client.post(`/api/todos`, {
      todo,
      completed: 0,
    }).then((result) => {
      if (result.data.hasOwnProperty('errors')) {
        this.setState({ errors: result.data.errors });
        return;
      }

      this.props.parent.addTodo(result.data.todo);

      this.setState({ errors: {} });
    });

    input.value = '';
  }

  render() {
    let { errors } = this.state;
    let errorKeys = Object.keys(errors);

    errors = errorKeys.length ?
      (
        <Alert danger dismissible>
          {errorKeys.map((field, i) => {
            return (
              <div key={i}>
                <div><strong>{field}:</strong></div>
                <ul>
                  {errors[field].map((error, j) => {
                    return <li key={j}>{error}</li>
                  })}
                </ul>
              </div>
            );
          })}
        </Alert>
      ) : null;

    return (
      <div>
        {errors}

        <Form horizontal onSubmit={::this.createTodo}>
          <FormGroup>
            <Col sm={10}>
              <FormControl type='text' placeholder='A todo item...' ref={(input) => this.input = input} autoFocus />
            </Col>
            <Col sm={2} collapseLeft>
              <br className='visible-xs' />
              <Button type='submit' bsStyle='blue' block onlyOnHover>Create Todo</Button>
            </Col>
          </FormGroup>
        </Form>
      </div>
    );
  }
}

The above code implements a TodoForm component that renders a form to create Todo items. It implements a createTodo method that is called when the form is submitted. The createTodo method executes a client.post request to /api/todos, with todo and completed passed as data. Again, as defined in the previous section, the dingo API route that corresponds to a POST request is /api/todos. If the creation failed due to an error in the backend (for instance: trying to save an empty todo), the then callback returns the result with errors encountered. If the todo item was successfully saved to the table we call the parent component's addTodo method with the result. The parent passed in a reference to itself as a prop while rendering the TodoForm component.

Now that we have created our basic components, it's time to create pages that render these components. This is detailed in the next section.