Laravel How to avoid page reloading when using QueryBuilder

322 Views Asked by At

On my laravel-application I use the spatie/laravel-query-builder to filter a list of jobs by some categories.

So my code looks like this for now:

My Controller:

public function index(Request $request) {

    $regions = Region::all();
    $jobtypes = JobType::all();
    $industries = Industry::all();

    $jobs = QueryBuilder::for(Job::class)
        ->allowedFilters([
            AllowedFilter::exact('region', 'region_id'),
            AllowedFilter::exact('jobtype', 'job_type_id'),
            AllowedFilter::exact('industry', 'industry_id')
        ])
        ->get();

    return view('job.index', compact('jobs', 'regions', 'jobtypes', 'industries'));
}

my blade view:

<form method="GET" id="jobfilter-form" enctype="multipart/form-data" content="{{ csrf_token() }}">
   <div>
    <p>Location</p>
    @foreach ($regions as $region)
    <label for="{{$region->id}}">
        <input type="checkbox" class="chk-filter" name="region" value="{{$region->id}}" />
        @if (in_array($region->id, explode(',', request()->input('filter.region'))))
        checked
        @endif
        {{$region->name}}
    </label>
    @endforeach
  </div>

  <div>
    <p>Job type</p>
    @foreach ($jobtypes as $jobtype)
    <label for="{{$jobtype->id}}">
        <input type="checkbox" class="chk-filter" name="jobtype" value="{{$jobtype->id}}" />
        @if (in_array($jobtype->id, explode(',', request()->input('filter.jobtype'))))
        checked
        @endif
        {{$jobtype->name}}
    </label>
    @endforeach
  </div>

  <div>
    <p>Industry</p>
    @foreach ($industries as $industry)
    <label for="{{$industry->id}}">
        <input type="checkbox" class="chk-filter" name="industry" value="{{$industry->id}}" />
        @if (in_array($industry->id, explode(',', request()->input('filter.industry'))))
        checked
        @endif
        {{$industry->name}}
    </label>
    @endforeach
  </div>

  <div>
    <button type="submit" id="filter">filter</button>
  </div>
</form>

and finally the javascript:

function getIds(checkboxName) {
  let checkBoxes = document.getElementsByName(checkboxName);
  let ids = Array.prototype.slice
    .call(checkBoxes)
    .filter(ch => ch.checked == true)
    .map(ch => ch.value);
  return ids;
}

function filterResults() {
  let regionIds = getIds("region");

  let jobtypeIds = getIds("jobtype");

  let industryIds = getIds("industry");

  let href = "filter?";

  if (regionIds.length) {
     href += "filter[region_id]=" + regionIds;
  }

  if (jobtypeIds.length) {
     href += "&filter[job_type_id]=" + jobtypeIds;
  }

  if (industryIds.length) {
    href += "&filter[industry_id]=" + industryIds;
  }

  document.location.href = href;
}


$("#jobfilter-form #filter").on("click", e => {
  filterResults();
});

This basically works fine but this solution (of course) makes the page reload each time the submit button has been clicked and also unchecks all previous checked checkboxes.

So my question is: Can I avoid the page been reloaded? and how can I keep the checked checkboxes?

Another thing is, that I was thinking of using something like this:

$(".chk-filter").on("change", function() {
   if (this.checked) {
     $('#jobfilter-form button[type="submit"]').click();
   }
});

and hide the submit button.

Can someone help me out?

1

There are 1 best solutions below

0
On

When you update the URL and cause it to navigate, this will likely reload the page. Better add an extra route, which accepts these parameters and returns JSON. Then you can convert your web 1.0 style into a dynamic form, which fetches with XHR and then manipulates the DOM accordingly. The CSRF token needs to be passed along with the XHR - but a form is generally not required, because JS would fetch JSON & update DOM (which would be the area which shows the results). When never submitting any form, the page won't reload and the check-boxes will stay as they are.