Skip to content
Advertisement

Laravel foreach loop malfunctioning with old() data

I have a form that allows users to add a new row of inputs by clicking a button. For some reason on form submit (with a required row purposefully left empty to test form repopulation), index #1 is being skipped, and it happens every time the form is submitted. Also, extra rows are added on submit, and I can’t seem to figure out why.

Here is a numbered (top right) gallery depicting the result.

Foreach Loop:

@foreach(old() as $key => $value)
          @if($key !== '_token' && $key !== 'Save' && $key !== 'due_by')
            <div class="grid grid-cols-11 gap-6">
              <div class="col-span-3">
                <label class="block font-medium text-sm text-gray-700" for="">Title</label>
                <input autocomplete="off" class="form-input rounded-md shadow-sm mt-1 block w-full" value="{{old('title.'.$loop->index)}}" type="text" name="title[]">
              </div>

              <div class="col-span-3">
                <label class="block font-medium text-sm text-gray-700" for="">Description</label>
                <input autocomplete="off" class="form-input rounded-md shadow-sm mt-1 block w-full" value="{{old('description.'.$loop->index)}}" type="text" name="description[]">
              </div>

              <div class="col-span-1">
                <label class="block font-medium text-sm text-gray-700" for="">Quantity</label>
                <input autocomplete="off" class="form-input rounded-md shadow-sm mt-1 block w-full" value="{{old('quantity.'.$loop->index)}}" type="text" name="quantity[]">
              </div>

              <div class="col-span-1">
                <label class="block font-medium text-sm text-gray-700" for="">Price</label>
                <input autocomplete="off" class="form-input rounded-md shadow-sm mt-1 block w-full" value="{{old('price.'.$loop->index)}}" type="text" name="price[]">
              </div>

              <div class="col-span-1">
                <label class="block font-medium text-sm text-gray-700" for="">Discount</label>
                <input autocomplete="off" class="form-input rounded-md shadow-sm mt-1 block w-full" value="{{old('discount.'.$loop->index)}}" type="text" name="discount[]">
              </div>

              <div class="col-span-1 mx-auto">
                <label class="block font-medium text-sm text-gray-700" for="taxable[{{$loop->index}}]">Taxable</label>
                <div class="input-container">
                  <input autocomplete="off" type="hidden" name="taxable[{{$loop->index}}]" value="off" />
                  <input autocomplete="off" type="checkbox" class="toggle-input" id="taxable[{{$loop->index}}]" name="taxable[{{$loop->index}}]" value="on" @if(old('taxable.'.$loop->index) == 'on') checked @endif>
                  <label class="toggle switch" for="taxable[{{$loop->index}}]" style="margin-top: 10px;">
                    <div class="toggle-inner"></div>
                  </label>
                </div>
          </div>

                <div class=" deleteField col-span-1 mx-auto"> <button style="margin-top:30px;" class="inline-flex items-center px-4 py-2 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest bg-red-600 hover:bg-red-500 transition duration-300 ease-in-out transform hover:-translate-y-1 hover:scale-115"><i class="fas fa-trash-alt fa-lg"></i> </button></div>
              </div>
          @endif
          @endforeach

jQuery:

$(document).ready(function() {
var max_fields = 100;
var wrapper = $(".container1");
var add_button = $(".add_form_field");

var x = 0;
$(add_button).click(function(e) {
    e.preventDefault();
    if (x < max_fields) {
        x++;
        $(wrapper).append('html here');
    }
});

Advertisement

Answer

This is likely due to how you’re looping the old data. Rather than looping through a subset of records specific for your table, you’re looping through the entire input data. Which you’ve already noticed, hence the conditional (if) checks to skip through keys you don’t need like _token.

Unfortunately, if an unwanted key like _token happens to be on the loop’s second run (index #1), nothing will be output and that row will be skipped. You can probably confirm this by confirming the output of old(), and seeing what order it’s in before rendering anything.

A couple better options might include:

  1. Looping through a known table value:

    @for ($i = 0, $count = old('title'); $i < $count; $i++)
    

    This targets only your “title” fields to count how many rows there are, but may produce inaccurate results if you allow titles to be blank and they aren’t received by your back end.

  2. Using nested structure naming for your form fields:

    <input name="rows[0][title]" value="{{ old('rows.0.title') }}" />
    <input name="rows[0][description]" value="{{ old('rows.0.description') }}" />
    

    This may take a little more work to get your data just right, and possibly change your back end to work with the new hierarchy. It will, however, simplify your template logic:

    @foreach (old('rows') as $row)
      {{ $row['title'] }}<br>
      {{ $row['description'] }}
    @endforeach
    

Pros and cons to each, use whichever suits your application or personal style the best. Either way, narrow in what data you’re looping and you’ll be successful. 🙂 Happy coding!

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement