Building a Blog With Reactjs And Laravel finishing website

Building a Blog With Reactjs And Laravel Part13: Wrapping up

This is the last article in this series and we will complete the remaining pieces such as the registration, login and commenting functionality.

 

 

 

Authentication

Before creating the comments functionality we need to create the authentication functionality so we have to enable the user to register and login.

Create files Auth.js and Comment.js in resources/js/website/apis/ and update them as shown below:

Auth.js

import axios from "axios";

const Auth = {
  login: (data, successCb, failCb) => {
      axios.post('/login', data).then(response => {

          successCb(response);

      }).catch(err => {

          failCb(err);
      });
  },
  logout: (successCb, failCb) => {
      axios.get('/logout', {headers: {Authorization: 'Bearer ' + localStorage.getItem("user.api_token")}})
          .then(response => {
              localStorage.clear();

              successCb(response);
          }).catch(err => {
              failCb(err);
          alert(err.response.data.message);
      });
  },
  register: (data, successCb, failCb) => {
      axios.post('/register', data)
          .then(response => {
            successCb(response);
          }).catch(err => {
            failCb(err);
      });
  }
};

export default Auth;

Comment.js

import axios from 'axios';

const Comment = {
    store: (payload) => {
        return axios.post('/comments', payload, {headers: {Authorization: 'Bearer ' + localStorage.getItem("user.api_token")}});
    }
};

export default Comment;

Well, the next step is to update the register and login components we have done similar thing in the admin panel before.

Update resources/js/website/components/pages/Register.js as follows:

import React from 'react';
import Sidebar from '../partials/Sidebar';
import {Link} from "react-router-dom";
import Auth from '../../apis/Auth';
import { withRouter } from "react-router";
import "../../css/form.css";

class Register extends React.Component
{
    constructor(props)
    {
        super(props);

        this.state = {
            name: "",
            email: "",
            password: "",
            error_message: null,
            errors: null
        };

        this.handleSubmit = this.handleSubmit.bind(this);

        this.handleInput = this.handleInput.bind(this);
    }

    handleInput(e) {
        this.setState({
            [e.target.name]: e.target.value
        });
    }

    handleSubmit(e) {
        e.preventDefault();

        this.setState({
            error_message: null,
            errors: null
        });

        Auth.register({name: this.state.name, email: this.state.email, password: this.state.password}, (response) => {

            for (var i in response.data.user) {
                localStorage.setItem("user." + i, response.data.user[i]);

                setTimeout(() => {
                    this.props.history.push("/");
                }, 500);
            }

        }, (err) => {
            this.setState({
                error_message: err.response.data.message,
                errors: err.response.data.errors
            });
        });
    }

    render()
    {
        return (
            <div id="content-wrap">
                <div className="row">
                    <div id="main" className="eight columns">

                        <h2>Create account</h2>

                        {
                            this.state.error_message?(<div className="alert alert-danger">{this.state.error_message}</div>):null
                        }

                        <form name="contactForm" method="post" onSubmit={this.handleSubmit}>
                            <fieldset>
                                <div className="group">
                                    <label>Username</label>
                                    <input name="name" type="text" onChange={this.handleInput} value={this.state.name} placeholder="Name" />
                                    {
                                        this.state.errors && this.state.errors.name?(<div className="error-block">{this.state.errors.name[0]}</div>):null
                                    }
                                </div>
                                <div className="group">
                                    <label>Email</label>
                                    <input name="email" type="text" onChange={this.handleInput} value={this.state.email} placeholder="Email" />
                                    {
                                        this.state.errors && this.state.errors.email?(<div className="error-block">{this.state.errors.email[0]}</div>):null
                                    }
                                </div>
                                <div className="group">
                                    <label>Password</label>
                                    <input name="password" type="password" onChange={this.handleInput} value={this.state.password} placeholder="Password" />
                                    {
                                        this.state.errors && this.state.errors.password?(<div className="error-block">{this.state.errors.password[0]}</div>):null
                                    }
                                </div>

                                <button type="submit" className="submit">Register</button>
                                &nbsp;<Link to="/login">Already have account</Link>
                            </fieldset>
                        </form>

                    </div>

                    <Sidebar/>

                </div>
            </div>
        )
    }
}

export default withRouter(Register);

Also update resources/js/website/components/pages/Login.js as follows:

import React from 'react';
import Sidebar from '../partials/Sidebar';
import {Link} from "react-router-dom";
import Auth from '../../apis/Auth';
import { withRouter } from "react-router";
import "../../css/form.css";

class Login extends React.Component
{
    constructor(props)
    {
        super(props);

        this.state = {
            email: "",
            password: "",
            error_message: null,
            errors: null
        };

        this.handleSubmit = this.handleSubmit.bind(this);

        this.handleInput = this.handleInput.bind(this);
    }

    handleInput(e) {
        this.setState({
            [e.target.name]: e.target.value
        });
    }

    handleSubmit(e) {
        e.preventDefault();

        this.setState({
            error_message: null,
            errors: null
        });

        if(this.state.email == "" || this.state.password == "") {
            this.setState({
                error_message: "Please enter login credentials"
            });

            return false;
        }

        Auth.login({email: this.state.email, password: this.state.password}, (response) => {

            for (var i in response.data.user) {
                localStorage.setItem("user." + i, response.data.user[i]);

                setTimeout(() => {
                    this.props.history.push("/");
                }, 500);
            }

        }, (err) => {
            this.setState({
                error_message: err.response.data.message,
                errors: err.response.data.errors
            });
        });
    }

    render()
    {
        return (
            <div id="content-wrap">
                <div className="row">
                    <div id="main" className="eight columns">

                        <h2>Login</h2>

                        {
                            this.state.error_message?(<div className="alert alert-danger">{this.state.error_message}</div>):null
                        }

                        <form name="contactForm" method="post" action="" onSubmit={this.handleSubmit}>
                            <fieldset>
                                <div className="group">
                                    <label>Email</label>
                                    <input name="email" type="text" onChange={this.handleInput} value={this.state.email} placeholder="Email" />
                                    {
                                        this.state.errors && this.state.errors.email?(<div className="error-block">{this.state.errors.email[0]}</div>):null
                                    }
                                </div>
                                <div className="group">
                                    <label>Password</label>
                                    <input name="password" type="password" onChange={this.handleInput} value={this.state.password} placeholder="Password" />
                                    {
                                        this.state.errors && this.state.errors.password?(<div className="error-block">{this.state.errors.password[0]}</div>):null
                                    }
                                </div>

                                <button type="submit" className="submit">Login</button>
                                &nbsp;<Link to="/register">Create account</Link>
                            </fieldset>
                        </form>

                    </div>

                    <Sidebar/>

                </div>
            </div>
        )
    }
}

export default withRouter(Login);

resources/js/website/css/form.css

.alert-danger {
    color: red;
}

.alert-success {
    color: green;
}

.error-block {
    color: red;
}

.auth-links a {
    color: orangered !important;
    font-size: 10px !important;
}

#comments form label {
    float: left !important;
}

#comments form .message label {
    display: block !important;
}

Then let’s add the logout functionality, this is done in the Header component so open resources/js/website/components/partials/Header.js and update it as shown:

import React from 'react';
import {Link} from "react-router-dom";
import { withRouter } from "react-router";
import GlobalContext from '../../GlobalContext';
import "../../css/form.css";
import Auth from '../../apis/Auth';

class Header extends React.Component
{
    constructor(props)
    {
        super(props);

        this.state = {
            category_id: ""
        };

        this.handleLogout = this.handleLogout.bind(this);
    }

    handleLogout(e) {
        e.preventDefault();

        Auth.logout((response) => {
            this.props.history.push("/");
        }, (err) => {
            alert(err.response.data.message);
        });
    }

    componentDidMount()
    {
        if(this.props.location.pathname.indexOf("category") !== -1) {
            let path_parts = this.props.location.pathname.split("/");

            // category_id is at index 2
            this.setState({
                category_id: path_parts[2]
            });
        } else {
            this.setState({
                category_id: ""
            });
        }
    }

    componentDidUpdate(prevProps)
    {
        if (prevProps !== this.props) {
            if (this.props.location.pathname.indexOf("category") !== -1) {
                let path_parts = this.props.location.pathname.split("/");

                // category_id is at index 2
                this.setState({
                    category_id: path_parts[2]
                });
            } else {
                this.setState({
                    category_id: ""
                });
            }
        }
    }

    render()
    {
        return (
            <header id="top">

                <div className="row">

                    <div className="header-content twelve columns">

                        <h1 id="logo-text"><Link to="/">React Laravel Blog</Link></h1>
                        <p id="intro">Excellence and Perfection</p>

                    </div>

                </div>

                <nav id="nav-wrap"><a id="toggle-btn" title="Menu" href="#">Menu</a>


                    <div className="row">

                        <ul id="nav" className="nav">
                            <li className={this.props.location.pathname=='/'?'current':''}><Link to="/">Home</Link></li>
                            {
                                this.context.categories.map(category => <li className={this.state.category_id==category.id?'current':''} key={category.id}><Link to={'/category/' + category.id + '/' + category.slug}>{ category.title }</Link></li>)
                            }

                            {
                                localStorage.getItem("user.api_token")!=null?(
                                    <li className="auth-links">
                                        <a href='#' onClick={this.handleLogout}>logout</a>
                                    </li>
                                ) : (
                                    <li className="auth-links">
                                        <Link to='/login'>Login</Link>
                                        <Link to='/register'>Register</Link>
                                    </li>
                                )
                            }
                        </ul>


                    </div>

                </nav>

            </header>
        )
    }
}

Header.contextType = GlobalContext;

export default withRouter(Header);

Commenting

Update resources/js/website/components/partials/CommentForm.js like so:

import React from 'react';
import { withRouter } from "react-router";
import '../../css/form.css';
import CommentApi from '../../apis/Comment';

class CommentForm extends React.Component
{
    constructor(props)
    {
        super(props);

        this.state = {
            comment: "",
            error_message: null,
            errors: null,
            success_message: null
        };

        this.handleSubmit = this.handleSubmit.bind(this);

        this.handleInput = this.handleInput.bind(this);
    }

    handleInput(e) {
        this.setState({
            comment: e.target.value
        });
    }

    handleSubmit(e) {
        e.preventDefault();

        this.setState({
            error_message: null,
            errors: null
        });

        CommentApi.store({comment: this.state.comment, post_id: this.props.match.params.id}).then(response => {

            this.setState({
                success_message: response.data.message
            });

        }).catch(err => {
            this.setState({
                error_message: err.response.data.message,
                errors: err.response.data.errors
            });
        });
    }

    render()
    {
        return (
            <div className="respond">

                <h3>Leave a Comment</h3>

                {
                    this.state.error_message?(<div className="alert alert-danger">{this.state.error_message}</div>):null
                }

                {
                    this.state.success_message?(<div className="alert alert-success">{this.state.success_message}</div>):null
                }

                <form name="contactForm" id="contactForm" method="post" onSubmit={this.handleSubmit}>
                    <fieldset>

                        <div className="message group">
                            <textarea name="comment" id="cMessage" rows="10" cols="50" placeholder="Add your comment" onChange={this.handleInput} value={this.state.comment}></textarea>
                            {
                                this.state.errors && this.state.errors.comment?(<div className="error-block">{this.state.errors.comment[0]}</div>):null
                            }
                        </div>

                        <button type="submit" className="submit">Submit</button>

                    </fieldset>
                </form>

            </div>
        );
    }
}

export default withRouter(CommentForm);

Now run

npm run dev

and go to the website then try to register, login, browse categories and posts. By completing the comment form we finished this series, i added the repository url in bitbucket below so you can clone and run it.

 

Repository in bitbucket

Clone the repository from this url. Do the following steps to get it up and running:

– Set your db settings in .env

-composer install

-npm install

-php artisan migrate

-npm run dev

 

 

Final thoughts

Up till now in this series you have learned how to build a Single page application using Reactjs, this series covered a lot of features in Reactjs such as dealing with components, using react router, most important using the Redux state manager as we implemented modules in admin panel. Also you see how to connect this with backend in this case laravel powers the backend of our application. Try to extend this series to add more functionality like using roles and permissions and much more.

 

 

0 0 vote
Article Rating
Share this: