1

I have never used ajax before, but in my current project, I'm seeing the need to. I have a table of invoices which has a field called "is_confirmed" which is set to false by default.

In the index.blade.php, I have displayed all invoices that was sent out by the currently logged in user. In each row of the table, once the user clicks the confirm button,that row is updated and the "is_confirmed" field is set to "true" in the database. The problem now is that they still have their confirm button active which means the user can still click it.

How would you implement this such that all rows whose "is_confirmed" field set to "true" will have their buttons disabled and those whose "is_confirmed" field set to "false" are the only ones with clickable button even upon page refresh.

Here's the displayed table: My table

Here's my index.blade.php that currently displays all sent invoices. Along with the confirm button to update the "is_confirmed" database field in each row:

        @section('content')
@foreach($sentinvoices as $sentinvoice)
    <tr>
        <td>
            <span>{{ $sentinvoice->recipient->fullname }}</span>
        </td>
        <td>
            <span>{{$sentinvoice->updated_at->format("M d, Y")}}</span>
        </td>
        <td>
            <span>{{$sentinvoice->status}}</span>
        </td>
        <td>
            <span>{{$sentinvoice->amount}}</span>
        </td>


<td class="text-center">
        <form method="POST" action="{{ route('sender.confirm', $sentinvoice->uuid) }}" id="ajax">
            @csrf
            <input type="hidden" name="sender_id" value="{{$sentinvoice->sender_id}}">
            <input type="hidden" name="is_confirmed" value="{{$sentinvoice->is_confirmed}}">
            <button class="btn btn-success btn-sm" type="submit" id="confirm">Confirm</button>
            </form>
    </td>
</tr>
@endforeach

@endsection

I also added ajax function at the bottom of the index.blade.php as follows:

@section('scripts')
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
<script>

    $("#ajax").click(function(event) {
        event.preventDefault();

        $.ajax({
            type: "post",
            url: "{{ url('sender.confirm') }}",
            dataType: "json",
            data: $('#ajax').serialize(),
            success: function(data){
                  alert("Data Save: " + data);
                  $("#confirm").prop('disabled', true);
            },
            error: function(data){
                 alert("Error")
            }
        });
    });
</script>
@endsection

Here's my function in the InvoiceController that does the form submission:

public function confirmInvoice(Request $request, $uuid)
    {   
        $user = auth()->user();
        $sentinvoices = Invoice::where('uuid', $uuid)->first();

        $sentinvoices->sender_id = $user->id;
        $sentinvoices->is_confirmed = 1;
        $sentinvoices->save();

        return redirect()->back();
    }

I have checked answers to other similar questions, still I couldn't get this to work. Please help me here.

2
  • Where is the code which renders the invoices? Commented Sep 21, 2018 at 13:16
  • I have updated the post by adding the code which renders the invoices. I'd really appreciate your help, thank you. Commented Sep 21, 2018 at 13:44

2 Answers 2

1

A couple of things here:

  1. Since you are making an AJAX request, the redirect response does not make any sense.
  2. Your #ajax id is actually the id of the form, you can't "click" on the form, but instead you submit it.
  3. Since you probably have multiple forms you should give each of them a unique identifier and listen for submit events on all of them.

Here's an example of what you can do:

View:

@section('content')
<form method="POST" action="{{ route('sender.confirm', $sentinvoice->uuid) }}" id="form-{{$sentinvoice->uuid}}" class='ajax'>
    @csrf
    <input type="hidden" name="sender_id" value="{{$sentinvoice->sender_id}}">
    <input type="hidden" name="is_confirmed" value="{{$sentinvoice->is_confirmed}}">
    <button class="btn btn-success btn-sm" type="submit" class="confirm" {!! $sentinvoice->is_confirmed ? 'disabled="disabled"' : '' !!}>Confirm</button>
</form>
@endsection

JavaScript

@section('scripts')
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
<script>

    $(".ajax").submit(function(event) { //listening on class name and for submit action
        event.preventDefault();
        var $confirmButton = $(this).find('.confirm'); // the confirm button of this form
       $confirmButton.prop('disabled', true);
        $.ajax({
            type: "post",
            url: $(this).attr('action'), //send to the correct url based on the markup
            dataType: "json",
            data: $(this).serialize(), //this refers to the submitted form
            success: function(data){
                  alert("Data Saved");
            },
            error: function(data){
                 $confirmButton.prop('disabled', false);
                 alert("Error")
            }
        });
    });
</script>
@endsection

And your controller:

public function confirmInvoice(Request $request, $uuid)
{   
    $user = auth()->user();
    $sentinvoices = Invoice::where('uuid', $uuid)->first();

    $sentinvoices->sender_id = $user->id;
    $sentinvoices->is_confirmed = 1;
    if (!$sentinvoices->save()) {
        return response()->json([ 'success' => false ], 500); //Error response
    }


    return response()->json([ 'success' => true ]); 
}

What changed:

  1. The ids are unique (and not really important) but we listen to events on classes. This is because JavaScript requires ids to be unique
  2. We listen to the submit event instead of the click event. This will cover alternative ways to submit a form (such as by tabbing to the button and pressing enter)
  3. The controller will return JSON and the appropriate response code.

Note that the confirm button here is disabled before the request is sent and enabled only if the request failed. This will prevent anyone from clicking it again before the request is done.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for your in-depth response. What I'm trying to do is not to only prevent the button from being clickable before the request is done. My problem is that, after the request is done, the button becomes clickable upon page refresh even for rows that has be previously confirmed. Think about it this way: When a user visits that page, the confirm button should only be active for invoices they haven't yet confirmed. All invoices that have been confirmed should not have an active button.
@Ehi try the update, that should be resolved by setting the disabled="disabled" attribute on the button based on the initial value of the model.
Thanks alot, this worked! I'm really grateful for your help @apokryfos. Thanks to everyone who took out time to help, I appreciate.
0

I'm assuming you have a form for each confirm button since you don't clarify that.

First of all your form and confirm button cannot have the id #ajax and #confirm because there will be more than one displayed on the page and element ids should be unique within the entire document.

So replace that with a class and remove the id on the confirm button like bellow:

@section('content')
<form method="POST" action="{{ route('sender.confirm', $sentinvoice->uuid) }}" class="confirm-form">
    @csrf
    <input type="hidden" name="sender_id" value="{{$sentinvoice->sender_id}}">
    <input type="hidden" name="sender_confirmed" value="{{$sentinvoice->is_confirmed}}">
    <button class="btn btn-success btn-sm" type="submit">Confirm</button></form>
@endsection

Then on your ajax request replace the id with the class name, change the event to submit and use jQuery and $(this) the current element to find the button inside it and set the prop disabled.

    $(".confirm-form").on('submit', function(event) {
        event.preventDefault();
        var $button = $(this).find('button');
        var data = $(this).serialize();
        $.ajax({
            type: "post",
            url: "{{ url('sender.confirm') }}",
            dataType: "json",
            data: data,
        }).done(function () {
          $button.prop('disabled', true)
        });
    });

Heres a simplified working example:

$(".confirm-form").on('submit', function(event) {
    event.preventDefault();
    var $button = $(this).find('button');
    var data = $(this).serialize();
    $.ajax({
        type: "post",
        url: "https://jsonplaceholder.typicode.com/todos/",
        dataType: "json",
        data: data,
    }).done(function () {
      $button.prop('disabled', true)
    });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<table>
  <tr>
    <td>Confirm 1</td>
    <td>
      <form class="confirm-form">
        <button>Confirm</button>
      </form>
    <td/>
  </tr>
  <tr>
    <td>Confirm 2</td>
    <td>
      <form class="confirm-form">
        <button>Confirm</button>
      </form>
    <td/>
  </tr>
  <tr>
    <td>Confirm 3</td>
    <td>
      <form class="confirm-form">
        <button>Confirm</button>
      </form>
    <td/>
  </tr>
</table>

1 Comment

Thanks for your prompt response. I do not have a form for each confirm button. I'll edit the post to clarify that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.