Boostrap style

Table des matières

1. Update master.blade.php

Edit /resources/views/master.blade.php and add external css. Update the <head> section like this:

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="csrf-token" content="{{ csrf_token() }}">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Some stupid Todos application</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css" integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">
  <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>

  <style>textarea { max-height:150px; }</style>
</head>

2. Update create.blade.php

That view is responsible for showing the creation form that allow to add new todo.

Edit /resources/views/create.blade.php and update how the submit button is made. Add also a back button and remove the content of the navigation section:

{!! Form::submit('Submit !', array('class' => 'btn btn-sm btn-primary')) !!}

<a href="javascript:location.href='{{ route('todos.index') }}'" class="btn btn-sm btn-success">
  <span class="glyphicon glyphicon-circle-arrow-left"></span> Back
</a>
@section('navigation')
@endsection

3. Update edit.blade.php

That view is responsible for showing the update form that allow to edit existing todo.

Edit /resources/views/edit.blade.php and update how the submit button is made. Add also a back button and remove the content of the navigation section:

{!! Form::submit('Submit !', array('class' => 'btn btn-sm btn-primary')) !!}

<a href="javascript:location.href='{{ route('todos.index') }}'" class="btn btn-sm btn-success">
  <span class="glyphicon glyphicon-circle-arrow-left"></span> Back
</a>
@section('navigation')
@endsection

4. Update index.blade.php

That view is responsible for showing the list of Todos.

For each todo, we’ll add button for:

Edit /resources/views/index.blade.php and update the foreach construction:

@if(session()->has('ok'))
    <div class="alert alert-success alert-dismissible">{!! session('ok') !!}</div>
@endif

@foreach($datas as $data)
<h3><a href="{{ route('todos.show', ['id' => $data->id]) }}">#{{ $loop->iteration. " - " . $data->title }}</a></h3>

<div style="padding:5px 0 15px 0;">
    <button type="submit" class="btn btn-sm btn-success"
        onclick="location.href='{{ route('todos.show', ['id' => $data->id]) }}'">
        <i class="glyphicon glyphicon-eye-open"></i> Show
    </button>

    <button type="submit" class="btn btn-sm btn-primary"
        onclick="location.href='{{ route('todos.edit', ['id' => $data->id]) }}'">
        <i class="glyphicon glyphicon-edit"></i> Edit
    </button>

    {!! Form::open(['method' => 'DELETE', 'route' => ['todos.destroy', $data->id], 'style' => 'display:inline']) !!}

    <button type="submit" class="btn btn-sm btn-danger delete" onclick="return confirm('Remove this record?')">
        <i class="glyphicon glyphicon-remove"></i> Delete
    </button>

    {!! Form::close() !!}
</div>
<p>{{ $data->description }}</p>
<small>Author: {{ $data->user->name }}</small>
<hr/>
@endforeach

This change implements three buttons between the title and the description of the todo:

Buttons

Note: for the Delete action, we need to create a form since, except with Ajax, only a form can send a DELETE header to the server.

The destroy() function of the controller return a ok message:

return redirect()->route('todos.index')->withOk('Successfully removed');

That message can be displayed in the view by using the Session:

@if(session()->has('ok'))
    <div class="alert alert-success alert-dismissible">{!! session('ok') !!}</div>
@endif

Deleted

5. Update show.blade.php

The Show view will display a given todo. Add bootstrap style to that view.

Edit /resources/views/show.blade.php and replace with this:

@extends('master')

@section('content')

  <div class="msg hide alert alert-success alert-dismissible">&nbsp;</div>

  {{--
    Display the detail of a todo; make sure we've one
  --}}
  @isset($data)
    {{--
      Show informations like title, description and timestamps
    --}}
    <h3>{{ $data->title }}</h3>
    <p>{{ $data->description }}</p>
    <small>
      Created at: {{ $data->created_at }}
      <br/>
      Last updated: {{ $data->updated_at }}
      <br/>

      {{--
        $data->user isn't a column but, in our model, the user() function
        returns an object which represent a record of the users table.
        So, through $data->user we can access to the user's name, email, ...
      --}}
      Author: {{ $data->user->name }}
    </small>

  @endisset

@endsection

@section('navigation')
  {{--
    Only for logged-in users, show action's buttons
  --}}
  @if(Illuminate\Support\Facades\Auth::check())
    <a href="javascript:history.back()" class="back btn btn-sm btn-success">
      <span class="glyphicon glyphicon-circle-arrow-left"></span> Back
    </a>
    <span class="buttons">
      <button class="btn btn-sm btn-primary edit">
        <i class="glyphicon glyphicon-edit"></i> Update
      </button>
      <button class="btn btn-sm btn-danger delete">
        <i class="glyphicon glyphicon-remove"></i> Delete
      </button>
    </span>
  @endif
@endsection

@section('script')
{{--
  Add our script for our buttons
--}}
<script defer="defer">
  $('.delete, .edit').click(function(){

    if ($(this).hasClass('delete')) {

      // Add the csrf-token protection but only when the request is
      // made on the same site (no cross-domain).
      // Don't share the token outside
      $.ajaxSetup({
        beforeSend: function(xhr, type) {
          if (!type.crossDomain) {
              xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
          }
        }
      });

      // By clicking on the delete button, make an Ajax request
      $.ajax({
        url: '{{ route('todos.destroy', $data->id) }}',
        type: 'DELETE',
        contentType: 'application/json',
        success: function (data) {
          if (data.hasOwnProperty("message")) {
            // Show the message
            $('.msg').html(data.message).removeClass('hide');
            // And remove buttons.
            $('.buttons').remove();
            // The back button should refresh the page so don't
            // use history.back() anymore
            $('.back').attr("href", "{{ route('todos.index') }}");
          }
        },
        error: function (data, textStatus, errorThrown) {
          console.log(data);
        }
      });
    } else {
      // The user has clicked on the edit button, redirect the browser
      // to the edit page
      window.location.replace('{{ route('todos.edit', ['id' => $data->id]) }}');
    }
});
</script>
@endsection

In the javascript section, we’ll replace the link for the Go back button when the user has clicked on the Delete. This because history.back() will show the page from the browser’s cache and therefore an obsolete page since the record has been removed. So, in this specific case, the page should be refreshed.

$('.back').attr("href", "{{ route('todos.index') }}");