0

I need some help in implementing a basic ajax request through vue to my laravel back-end, I have a boolean named completed on a table called courses, and I have a view that fetches all courses assigned to a specific user and allows them to press a button to change the current status of the course, either completed or not, that's it, that's all I wanna do, right now I'm half way there, I can submit the ajax request but it only grabs the last course that was fed to the view, I know this will eventually turn out to be a mistake from my end that I can't see, so please be explicit in your solution,

The relevant part of the CoursesController:

  public function toggling($name)
  {
    $course = Course::where(['name' => $name])->first();

    $course->completed = !$course->completed;
    $course->save();

    return response()->json(['course' => $course], 202);
    }

The Vue instance which is set on the main layout page(If this is a mistake please point that out):

<script>
 new Vue({
      el: '#app',
      data: {
        course: {
          name: ''
        }
      },
      methods: {
        onSubmit: function($course) {
          this.course.name = '{{$course->name}}',
          axios.post('/MyCourses/' + this.course.name);
              //  .then(function (response){
              //  });
        }
      }
  });
</script>

The form part of the view responsible of rendering the courses:

<tbody>
    @foreach ($courses as $course)
      <tr>
        <td>{{ $course->name }}</td>
        <td>{{ $course->appointment }}</td>
        <td>{{ $course->room_id }}</td>
        <td>
           <form method="POST" action="{{ route('course.completed', $course->name) }}" id="form-submit">
               {{ csrf_field() }}
                   @if ($course->completed == true)
                     <button @click.prevent="onSubmit" type="button" class="btn btn-sm" id="coursetogglingtrue">Done!</button>
                   @else
                       <button @click.prevent="onSubmit" type="button" class="btn btn-sm" id="coursetogglingfalse">Not Yet!</button>
                   @endif
           </form>
       </td>
     </tr>
   @endforeach
</tbody>

If you have time I'd appreciate if you can help make this dynamic by binding the id of the divs to the appropriate class based on the status of the course, I cannot do that right now, tried and failed to do an if condition inside the Vue instance.

And this is the one route responsible for the request from web.php:

Route::post('/MyCourses/{name}', 'CoursesController@toggling')->name('course.completed');

Right now, let's say I have 4 courses on my page, If I pressed on any button, only the last course would be provided to the vue instance regardless of which button I pressed, I'm guessing it is probably the structure of my view is what's wrong, I'd like to know what's causing the problem, also my other pages are errorring out because they don't have an access to a $course object, which is why I assume that having all of this on the main Vue instance is a big no-no, once again, whatever ideas/tips/recommendations you have on the problem or on my code please let me know, thanks.

0

1 Answer 1

1

Firstly, the reason you're only getting the last name in your Vue instance is because you're hardcoding it with blade. When you use a foreach loop the last iteration will remain available in the script even after the loop is completed (in this case the last $course in the loop).

this.course.name = '{{$course->name}}', //<-- There shouldn't be a comma here

If you check the html in the console you will see the name being hardcoded (remember your PHP code will be evaluated on the server before it's sent to the browser). The reason it does appear until you click on a button is because the on submit function won't execute until then.

On way to get around your issue would be to pass the $course variable to onSubmit() i.e.:

@click.prevent="onSubmit({{ $course }})"

Then your onSubmit() method would be:

onSubmit: function (course) {
    axios.post('/MyCourses/' + course.name);
    // .then(function (response) {})
}

You would also need to add the csrf_field to the request. One easy way to do this would be to add the following in your <script> above new Vue:

window.axios.defaults.headers.common = {
    'X-CSRF-TOKEN': '{{ csrf_token() }}',
    'X-Requested-With': 'XMLHttpRequest'
};

For more information you might find this tutorial helpful: https://laracasts.com/series/learning-vue-step-by-step/episodes/10

Hope this helps!

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

3 Comments

Thank you so much for your answer, I was really sad because I couldn't figure it out and seemed liked such an easy thing and it is, but you sir, saved the day, I have a couple of questions tho, what about that csrf field? I have one already in my form on the blade template, or since the form never submits the csrf_field protection doesn't activate? second question can you help me with the design issue? I simply want a single button in my view and that button would depend on the status of the course.
I am doing this now but ofc getting unexpected end of file, I know something is wrong in my way of writing code but meh..if you can take a look, ` methods: { onSubmit: function(course) { axios.post('/MyCourses/' + course.name) .then(function (response){ @if (course.completed === true) this.cssClass = 'coursetogglingtrue', this.text = 'Done!' }); } } `
I want to do something similar to this, but I know this won't work properly because this means It will only be rendered when that method is called, and that's not what we want but it's the general direction

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.