kendrick's avatar

Filter Search on collection, logic

I am currently thinking about implementing a search bar to a collection of Users. Basically I am having a foreach-loop collection of e.g. users. At the top i would integrate a search bar, which would e.g. trigger the name, or username, and update the foreach loop based on the search-bar input.

Currently I am handling search, like so: (post method)

Model::where(DB :: raw("CONCAT(name, ' ', username)"), 'LIKE', "%{$query}%")
        ->orWhere('first_name', 'LIKE', "%{$query}%")
        ->get();

Now my question is, how do I approach this task? Is this only possible with a vue-compoment?

0 likes
15 replies
kendrick's avatar

I read about working it out through an ajax request, but somehow it doesn't work, yet.

This is the current setup

HomeController.php

public function search(Request $request){


    if($request->ajax()){

      $output="";

      $users=DB::table('users')->where('first_name','LIKE','%'.$request->search."%")->get();

      if($users){

        foreach ($users as $key => $user) {

          $output.='<tr>'.

          '<td>'.$user->id.'</td>'.

          '<td>'.$user->first_name.'</td>'.

          '</tr>';

        }

        return Response($output);

      }
    }
  }

web.php


Route::get('/searchusers','HomeController@search');

View:

<div class="form-group">

   <input type="text" class="form-controller" id="search" name="search"></input>

</div>

<table class="table table-bordered table-hover">

           <thead>

             <tr>

               <th>ID</th>

               <th>First Name</th>
 
             </tr>

           </thead>
</table>
<script type="text/javascript">
  $('#search').on('keyup',function(){
   
  $value=$(this).val();
   
  $.ajax({
   
  type : 'get',
   
  url : '{{URL::to('searchusers')}}',
   
  data:{'search':$value},
   
  success:function(data){
   
  $('tbody').html(data);
   
  }
   
  });
   
   
   
  })
   
  $.ajaxSetup({ headers: { 'csrftoken' : '{{ csrf_token() }}' } });
</script>

Are there any obvious mistakes?

realrandyallen's avatar

It would definitely be better in a Vue component since Vue is reactive but what you have should work. Personally if it were me I'd get your html out of the controller and just return the data and have your javascript populate the table.

public function search(Request $request) {
    if ($request->ajax()) {
        $users = DB::table('users')->where('first_name','LIKE','%'.$request->search."%")->get();

        return response()->json(['users' => $users], 200);
    }

    return something_else;
}
$('#search').on('keyup', function () {
    $.ajax({
        type : 'get',
        url : '{{URL::to('searchusers')}}',
        data:{ 'search': $(this).val() },
        success: function (data) {
            $('tbody').empty();
            
            $.each(data.users, function (user) {
                $('tbody').append("<tr><td>" + user.id + "</td><td>" + user.first_name + "</td></tr>");
            });
        }
    });
});

1 like
kendrick's avatar

Thanks again @realrandyallen

Tried to compile the component, I get Syntax Error: Unexpected token for the '{{URL::to('searchusers')}}', line.

kendrick's avatar

The component is now triggered within the view, but the instance has no reactive state. @realrandyallen

<template>
    <div class="container>
        <div class="form-group">

            <input type="text" class="form-controller" id="search" name="search"></input>

        </div>

        <table class="table table-bordered table-hover">

            <thead>

                <tr>

                    <th>ID</th>

                    <th>First Name</th>

                </tr>

            </thead>
        </table>
    </div>
</template>


<script>
    export default { 

        mounted() {
            console.log('Component mounted. ?')
        }
    };


    $('#search').on('keyup', function() {
      
       $.ajax({
           type : 'get',
           url : "{{URL::to('searchusers')}}",
           data:{ 'search': $(this).val() },
           success: function (data) {
               $('tbody').empty();
               
               $.each(data.users, function (user) {
                   $('tbody').append("<tr><td>" + user.id + "</td><td>" + user.first_name + "</td></tr>");
               });
           }
       });
     });

</script>
realrandyallen's avatar

I didn’t realize this was inside a Vue component - that kinda changes everything - you could move your jQuery code up inside the mounted function but this should be done differently in a Vue component

1 like
kendrick's avatar

I created a component, as you recommended it to better be in a Vue component, sorry for the confusion. @realrandyallen

lostdreamer_nl's avatar

@splendidkeen

When starting with Vue it can be hard to stop thinking in dom manipulating terms (jQuery).

Try this version:

<template>
    <div class="container>
        <div class="form-group">
          <input type="text" class="form-controller" v-model="search" @change="search"></input>
        </div>
        <table class="table table-bordered table-hover">
        <thead>
          <tr>
            <th>ID</th>
            <th>First Name</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="user in users">
            <td>@{{ user.id }}</td>
            <td>@{{ user.name }}</td>
          </tr>
        </tbody>
        </table>
    </div>
</template>
        

<script>
    export default { 
        data: {
             search: '',
             users: []
        },
        mounted() {
            console.log('Component mounted. ?')
        },
        methods: {
          search: function() {
            var self = this;
            $.ajax({
               type : 'get',
               url : "{{ URL::to('searchusers') }}",
               data:{ 'search': this.search },
               success: function (data) {
                   self.users = data.users;
           }
          }
        }
    };
</script>

So, you add the users array to Vue, and in the template loop over all users (there wont be any on load)

And we link the search field to the data.search key, and @change will run the search method every time the value changes.

Never try to manipulate the dom directly with Vue, just manipulate your dataset and have the DOM react to those changes instead.

kendrick's avatar

Thank you for your advice with the dom @lostdreamer_nl

Within my console I get two Vue:warns, and a jquery error

<script>
    export default { 
        data: {
             search: '',
             users: []
        },
        mounted() {
            console.log('Component mounted. ?')
        },

        methods: {
          search: function() {
            var self = this;
            $.ajax({
               type : 'get',
               url : "{{ URL::to('searchusers') }}",
               data:{ 'search': this.search },
               success: function (data) {
                   self.users = data.users;
           }
          });
        }
    }
};
</script>
 The "data" option should be a function that returns a per-instance value in component definitions.
Property or method "users" is not defined on the instance but referenced during render.

GET https://code.jquery.com/jquery-latest.js net::ERR_CONNECTION_REFUSED

and the input contains a value of

function () { [native code] }
lostdreamer_nl's avatar

Aah yes, I didnt see it was a component:

        data: {
             search: '',
             users: []
        },
// should become
        data: function() {
             return {
               search: '',
               users: []
             }
        },

The jquery one is ERR_CONNECTION_REFUSED, that's up to you, your internet, or that server to fix ;)

kendrick's avatar

Thanks for coming back @lostdreamer_nl

Now we get:

[Vue warn]: Method "search" has already been defined as a data property.

Uncaught TypeError: fns.apply is not a function

CONNECTION_REFUSED was based on an internet problem

lostdreamer_nl's avatar

I should really test instead of write :p

// rename search method to doSearch
        methods: {
          search: function() {
            var self = this;
            $.ajax({
               type : 'get',
               url : "{{ URL::to('searchusers') }}",
               data:{ 'search': this.search },
               success: function (data) {
                   self.users = data.users;
           }
          });
        }
// becomes 
        methods: {
          doSearch: function() {
            var self = this;
            $.ajax({
               type : 'get',
               url : "{{ URL::to('searchusers') }}",
               data:{ 'search': this.search },
               success: function (data) {
                   self.users = data.users;
           }
          });
        }

and

<input type="text" class="form-controller" v-model="search" @change="search"></input>
// becomes
<input type="text" class="form-controller" v-model="search" @change="doSearch"></input>
1 like
kendrick's avatar

Thanks, the changes worked, but as I try to search users with my method search()

public function search(Request $request) {

        if ($request->ajax()) {
            $users=DB::table('users')->where('first_name','LIKE','%'.$request->search."%")->get();

            return response()->json(['users' => $users], 200);

        }

        return view('/', compact('users'));
    }

Nothing will be triggered or show, the array accepts the input, but no users will be shown.

Are there problems with the function? @lostdreamer_nl

Console:

/%7B%7B%20URL::to('searchusers')%20%7D%7D?search=phi 404 (Not Found)
lostdreamer_nl's avatar
Level 53

Oooh right.... you're doing this in .vue files, not in blade....

Right now, it's trying to go to the URL /{{ URL::to('searchusers') }}?search=phi

You'll either have to hard code URL, or put it in a javascript variable in the DOM.

At this stage, I suggest to hard code it, and learn Vue first before going the long way of getting routes in javascript ;)

// so replace
            $.ajax({
               type : 'get',
               url : "{{ URL::to('searchusers') }}",
               data:{ 'search': this.search },
               success: function (data) {
                   self.users = data.users;
           }
// with
            $.ajax({
               type : 'get',
               url : "/search",
               data:{ 'search': this.search },
               success: function (data) {
                   self.users = data.users;
           }

Or whatever the URL is to the search page

1 like

Please or to participate in this conversation.