Image Description
Author
dev k
Published
Sep 2, 2019

SPRINGBOOT + REACT (CRUD)

In this example, we will create todo management application in react, We have following api's, all api's are created with springboot .

Api's Available

                                POST /todo/save

GET /todo/all

GET /todo/find/{id}

PUT /todo/update

DELETE /todo/delete/{id}


                            

SET UP RECT PROJECT

To Learn more about react, please visit REACT + HELLO WORLD

Once todo-management-fe is created, open created project in IDE

                                create-react-app todo-management-fe
                            

ADD BOOTSTRAP

Add bootstrap to our project, so that we can use bootstrap classes for styling

index.html

                                <!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="logo192.png" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>React App</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>

                            

CREATE STATEFUL COMPONENT

At this point, lets create a basic stateful react component, and plug this component in our main component which is App.js

TodoComponent.JS

                                import React from "react";

class TodoComponent extends React.Component{
    constructor(props){
        super(props)
    }

    render() {
        return(
            <div>
                <h2> Management App</h2>
            </div>
        )
    }
}

export default TodoComponent;

                            

App.js

                                import React from 'react';
import './App.css';
import TodoComponent from "./components/TodoComponent";

function App() {
  return (
    <div className="App">
      <TodoComponent/>
    </div>
  );
}

export default App;

                            

INSTALL AND INITIALIZE AXIOS

Lets install axios, so that we will be able to communicate to our spring boot rest service to get , save, update and delete resources.

To use axios in react, we simply need to import it. We will do this later when we will create services

                                npm install axios --save
                            

MAKE ADD TODO FORM

Created Simple add todo form using bootstrap classes, lets discuss what state and onchange methods are doing :

React uses special syntax called JSX.
It is a syntax extension to JavaScript. It is recommend using it with React to describe what the UI should look like. JSX may remind us of a template language, but it comes with the full power of JavaScript.

In HTML, form elements such as input, textarea, and select typically maintain their own state and update it based on user input. In React, mutable state is typically kept in the state property of components, and only updated with setState(). We can combine the two by making the React state be the “single source of truth”. Then the React component that renders a form also controls what happens in that form on subsequent user input. An input form element whose value is controlled by React in this way is called a “controlled component”.

Since the value attribute is set on our form element, the displayed value will always be this.state.todoTask and this.state.taskDate, making the React state the source of truth. Since handleChange runs on every keystroke to update the React state, the displayed value will update as the user types. With a controlled component, every state mutation will have an associated handler function. This makes it straightforward to modify or validate user input.

Also, the name attribute in form fields should be same as the state variables, this is required for multiple form inputs.

At this points we, are logging the user inputs value.

Also, added hard coded toto's, next we will call rest service with axios and make our form dynamic

TodoComponent.js

                                import React from "react";

class TodoComponent extends React.Component{
    constructor(props){
        super(props)
        this.state = {
            todoTask: '',
            taskDate: '',
           taskId: 0
        }
        this.handleChange = this.handleChange.bind(this);
        this.addTodo = this.addTodo.bind(this);
    }

    handleChange(event){
        this.setState({[event.target.name] : event.target.value})
    }

    addTodo(event){
        event.preventDefault();
        console.log(this.state)
    }


    render() {
        return(
            <div className="container">
                <div className="row mt-5">
                    <h2>TODO MANAGEMENT</h2>
                </div>
                <form onSubmit={this.addTodo}>
                   <div className="row">
                       <div className="col-md-10 text-left mt-5">
<div className="form-group">
                               <label>Todo task</label>
                               <input type="text" className="form-control" placeholder="todo id" name="todoId" value={this.state.todoId} onChange={this.handleChange}/>
                           </div>
                           <div className="form-group">
                                <label>Todo task</label>
                               <input type="text" className="form-control" placeholder="task" name="todoTask" value={this.state.todoTask} onChange={this.handleChange}/>
                           </div>
                           <div className="form-group">
                               <label>Todo task Date</label>
                               <input type="text" className="form-control" name="taskDate" value={this.state.taskDate} onChange={this.handleChange} placeholder="yyyy-mm-dd"/>
                           </div>
                           <input type="submit" className="btn btn-success" value="ADD TODO"/>
                       </div>
                   </div>
                </form>

                <div className="col-md-10">
                    <table className="table table-striped mt-5">
                        <thead>
                        <tr>
                            <th>ID</th>
                            <th>TASK NAME</th>
                            <th>TASK DATE</th>
                            <th></th>
                            <th></th>
                        </tr>
                        </thead>
                        <tbody>
                        <tr>
                            <td>1</td>
                            <td>Learn Spring</td>
                            <td>2019-09-01</td>
                        </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        )
    }
}

export default TodoComponent;

                            

CREATE SERVICE

Lets, create our services using axios. For this, we will create a new js file in which we will define methods to call to our spring boot rest api's.

TodoManagementService.js

                                import axios from 'axios';

class TodoManagementService {

    saveTodoService(todo) {
        return axios.post('http://localhost:8080/todo/save', todo);
    }

    getAllTodosService(){
        return axios.get('http://localhost:8080/todo/all');
    }

    getTodoById(id){
        return axios.get('http://localhost:8080/todo/find/'+id);
    }

    updateTodoService(todo){
        return axios.put('http://localhost:8080/todo/update',todo);
    }

    deleteTodoById(id){
        return axios.delete('http://localhost:8080/todo/delete/'+id);
    }

}

export default new TodoManagementService();

                            



Introduced life cycle hook : componentdidmount, and in this hook, we are calling get all todolist method.

The componentDidMount() method runs after the component output has been rendered to the DOM. This is a good place to set up a getAllTodos().

We also changed our implementation to dynamic rendering the todoList and added edit and delete buttons. We will click event on this buttons later

                                this.state = {
            todoTask: '',
            taskDate: '',
            allTodoArr: []
        }

------------------------

getAllTodos(){
        TodoManagementService.getAllTodosService().then(resp => {
            this.setState({allTodoArr: resp.data})
            console.log(this.state.allTodoArr)
        })
    }

    componentDidMount() {
        this.getAllTodos();
    }

-----------------------

 <table className="table table-striped mt-5">
                        <thead>
                        <tr>
                            <th>ID</th>
                            <th>TASK NAME</th>
                            <th>TASK DATE</th>
                            <th></th>
                            <th></th>
                        </tr>
                        </thead>
                        <tbody>
                        {
                            this.state.allTodoArr.map((todo) =>
                                <tr key={todo.todoId}>
                                    <td>{todo.todoId}</td>
                                    <td>{todo.todoTask}</td>
                                    <td>{todo.taskDate}</td>
                                    <td><button type="button" className="btn btn-info btn-sm">Edit</button></td>
                                    <td><button type="button" className="btn btn-danger btn-sm">Delete</button></td>
                                </tr>

                            )
                        }

                        </tbody>
 </table>
                            



Let's use saveTodoService() to create new todo, if response status is 201 (created), then we are calling getAllTodos() to refresh the all todo's list and display it

                                addTodo(event){
        event.preventDefault();
       TodoManagementService.saveTodoService(this.state).then(resp => {
           if(resp.status === 201){
               this.getAllTodos();
           }
       })
    }
                            



Let's define edit todo, we create new onclick method editTodo() which accepts an todoId, to send "id" as an parameter to editTodo() , we used arrow function, this is the reason we dont have to bind (this). Inside editTodo(), we are calling getTodoById service and setting the response to todoArr.

We are also displaying update button Conditionally, for that we introduced new boolean flag showUpdateBtn.

If showUpdateBtn is true then condition after "&&" will be true i.e Update Todo button will be visible and if showUpdateBtn is false the Add Todo will be visible

                                 this.state = {
            todoTask: '',
            taskDate: '',
            todoId: 0,
            allTodoArr: [],
            showUpdateBtn: false
        }

-----------------------------

editTodo(id){
        TodoManagementService.getTodoById(id).then(resp => {
            if(resp){
                this.setState({showUpdateBtn: true});
                this.setState({
                    todoId: resp.data.todoId,
                    todoTask: resp.data.todoTask,
                    taskDate: resp.data.taskDate
                })
            }
        })
    }

-------------------------

 {!this.state.showUpdateBtn &&
                                <input type="submit" className="btn btn-success" value="ADD TODO"/>
                           }

  {this.state.showUpdateBtn &&
                               <button type="button" className="btn btn-warning text-white ml-4" onClick={this.updateTodo}>UPDATE TODO</button>
                           }

------------------------

 <td><button type="button" className="btn btn-info btn-sm" onClick={() => this.editTodo(todo.todoId)}>Edit</button></td>

                            


In In update Todo Method, we are calling updateTodoService and if response in 200 (ok), then we are calling getAllTodo() to refresh the todo list and display it.

deleteTodo is same as editTodo(), its just we are calling deleteTodo() and passing id in parameter. Inside deleteTodo(), we are calling deleteTodoService and if the response is 200 (ok), then we are calling getAllTodo() to refresh the todo list and display it.

                                updateTodo(){
        TodoManagementService.updateTodoService(this.state).then(resp => {
            if(resp.status === 200){
                this.getAllTodos();
            }
        })
    }

----------------------------

deleteTodo(id){
        TodoManagementService.deleteTodoById(id).then(resp => {
            if(resp.status === 200){
                this.getAllTodos();
            }
        })
    }

---------------------------

<td><button type="button" className="btn btn-danger btn-sm" onClick={() => this.deleteTodo(todo.todoId)}>Delete</button></td>
                            

COMPLETE CODE

TodoComponent.js

                                import React from "react";
import TodoManagementService from "../services/TodoManagementService";

class TodoComponent extends React.Component{
    constructor(props){
        super(props)
        this.state = {
            todoTask: '',
            taskDate: '',
            todoId: 0,
            allTodoArr: [],
            showUpdateBtn: false
        }
        this.handleChange = this.handleChange.bind(this);
        this.addTodo = this.addTodo.bind(this);
        this.updateTodo = this.updateTodo.bind(this);
    }

    handleChange(event){
        this.setState({[event.target.name] : event.target.value})
    }

    addTodo(event){
        event.preventDefault();
       TodoManagementService.saveTodoService(this.state).then(resp => {
           if(resp.status === 201){
               this.getAllTodos();
           }
       })
    }

    getAllTodos(){
        TodoManagementService.getAllTodosService().then(resp => {
            this.setState({allTodoArr: resp.data})
        })
    }

    editTodo(id){
        TodoManagementService.getTodoById(id).then(resp => {
            if(resp){
                this.setState({showUpdateBtn: true});
                this.setState({
                    todoId: resp.data.todoId,
                    todoTask: resp.data.todoTask,
                    taskDate: resp.data.taskDate
                })
            }
        })
    }

    updateTodo(){
        TodoManagementService.updateTodoService(this.state).then(resp => {
            if(resp.status === 200){
                this.getAllTodos();
            }
        })
    }

    deleteTodo(id){
        TodoManagementService.deleteTodoById(id).then(resp => {
            if(resp.status === 200){
                this.getAllTodos();
            }
        })
    }

    componentDidMount() {
        this.getAllTodos();
    }


    render() {
        return(
            <div className="container">
                <div className="row mt-5">
                    <h2>TODO MANAGEMENT</h2>
                </div>
                <form onSubmit={this.addTodo}>
                   <div className="row">
                       <div className="col-md-10 text-left mt-5">
                           <div className="form-group">
                               <label>Todo task</label>
                               <input type="text" className="form-control" placeholder="todo id" name="todoId" value={this.state.todoId} onChange={this.handleChange}/>
                           </div>
                           <div className="form-group">
                                <label>Todo task</label>
                               <input type="text" className="form-control" placeholder="task" name="todoTask" value={this.state.todoTask} onChange={this.handleChange}/>
                           </div>
                           <div className="form-group">
                               <label>Todo task Date</label>
                               <input type="text" className="form-control" name="taskDate" value={this.state.taskDate} onChange={this.handleChange} placeholder="yyyy-mm-dd"/>
                           </div>

                           {!this.state.showUpdateBtn &&
                                <input type="submit" className="btn btn-success" value="ADD TODO"/>
                           }
                           {this.state.showUpdateBtn &&
                               <button type="button" className="btn btn-warning text-white ml-4" onClick={this.updateTodo}>UPDATE TODO</button>
                           }
                       </div>
                   </div>
                </form>

                <div className="col-md-10">
                    <table className="table table-striped mt-5">
                        <thead>
                        <tr>
                            <th>ID</th>
                            <th>TASK NAME</th>
                            <th>TASK DATE</th>
                            <th></th>
                            <th></th>
                        </tr>
                        </thead>
                        <tbody>
                        {
                            this.state.allTodoArr.map((todo) =>
                                <tr key={todo.todoId}>
                                    <td>{todo.todoId}</td>
                                    <td>{todo.todoTask}</td>
                                    <td>{todo.taskDate}</td>
                                    <td><button type="button" className="btn btn-info btn-sm" onClick={() => this.editTodo(todo.todoId)}>Edit</button></td>
                                    <td><button type="button" className="btn btn-danger btn-sm" onClick={() => this.deleteTodo(todo.todoId)}>Delete</button></td>
                                </tr>

                            )
                        }

                        </tbody>
                    </table>
                </div>
            </div>
        )
    }
}

export default TodoComponent;

                            

SPRING BOOT REST CONTROLLER

TodoManagementService.java

                                package com.devk.todo.todomanagement.controller;

import com.devk.todo.todomanagement.service.TodoManagementService;
import com.devk.todo.todomanagement.vo.Todo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("todo")
@CrossOrigin
public class TodoController {

    @Autowired
    private TodoManagementService todoManagementService;

    @GetMapping("/all")
    public List<Todo> getAllTodods(){
        return todoManagementService.allTodos();
    }

    @GetMapping("/find/{id}")
    public Todo findTodoById(@PathVariable("id") Long todoTid){
        return todoManagementService.findTodoById(todoTid);
    }

    @PostMapping("/save")
    public ResponseEntity<?> saveTodo(@RequestBody Todo todo){
        todoManagementService.addTodo(todo);
        return new ResponseEntity<>(HttpStatus.CREATED);
    }

    @PutMapping("/update")
    public ResponseEntity<?> updateTodo(@RequestBody Todo todo){
        todoManagementService.updateTodo(todo);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @DeleteMapping("/delete/{id}")
    public ResponseEntity<?> deleteTodo(@PathVariable("id") Long todoId){
        todoManagementService.deleteTodoById(todoId);
        return new ResponseEntity<>(HttpStatus.OK);
    }

}

                            

PLEASE COMMENT IF YOU HAVE ANY QUESTIONS

Comments


Ask Question/Share Feedback

Comment
Name
Email