kendrick's avatar

Resource instead of response()->json()

I implemented a doSearch method within a component for users.

Within the component, I am first getting all users (getUsers()), and the search method effects this users loop. The problem is, that the getUsers() method is using return UserResource::collection($users); which triggers e.g. user-related info.

<div v-for="friend in users" :key="friend.id">

Now, if I am searching a user, the method works, I am getting the correct User in an array (Vue console), but the output at <div v-for="friend in users" :key="friend.id"> will be blank (e.g no first_name displayed), because I am using:

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

which lacks the information structured in the UserResource

Is there a way to solve this, so that the doSearch results will use the UserResource, or am I doing something wrong?

0 likes
23 replies
Nakov's avatar

@splendidkeen and why not returning the same UserResource from the doSearch method?

$filteredUsers = // perform the filter based on the search


return UserResource::collection($filteredUsers); 

This does not work?

kendrick's avatar

@nakov - Yes, already tried it. It throws a component related error like: "TypeError: undefined is not an object (evaluating 'this.users.some')"

Nakov's avatar

@splendidkeen for some reason your this.users is undefined. So in your Vue component do you set the result from the response to the this.users the same way as when you call the getUsers() method?

kendrick's avatar

These are the methods:

doSearch: function() {
      var self = this;
      $.ajax({
       type : 'get',
       url : "/search",
       data:{ 'search': this.search },
       success: function (data) {
         self.users = data.users;
       }
     });
    },

    getUsers() {
      axios.post("/getUsers").then(res => {
        this.users = res.data.data;
        this.users.forEach(
          friend => (friend.session ? this.listenForEverySession(friend) : "")
          );
      });
    },

and data()

data(){
    return{  
      search: '',
      users: []
    }
  },
Nakov's avatar

Try adding this:

success: function (data) {
    console.log(data);
    self.users = data.users;
}

when you do the search, and make sure you get the data correctly.

kendrick's avatar

I get the correct data (with return response()->json(['users' => $users], 200);)

Object
users: Array (1)
0 Object -> shows the basic User model - all records
Nakov's avatar

@splendidkeen you keep using the old way.. but you said this:

which lacks the information structured in the UserResource

So I am saying that you need to return the UserResource with the filtered collection, so you get the same response as with the one that works.

kendrick's avatar

Ah - found the troublemaker. * Now with return UserResource::collection($users); - it also console.logs the correct user. But, the error is cause by a computed method:

"TypeError: undefined is not an object (evaluating 'this.users.some')"

atLeastOneOpened() {
      return this.users.some(friend => friend.session && friend.session.open);
},

Because I am using this logic:

<div v-for="friend in users" :key="friend.id">
	<div v-if="friend.session && friend.session.open && atLeastOneOpened">
		... show user-related content
	</div>
	<div v-if="!atLeastOneOpened">
		...show user info
	</div>
</div>

How can I solve it?

Nakov's avatar

As it is undefined than you should check if there are any users before you do the logic, maybe this will do it:

atLeastOneOpened() {
      return this.users !== undefined && this.users.some(friend => friend.session && friend.session.open);
},
kendrick's avatar

Not yet, it console.logs the correct user, but hides the user within the users loop. I updated the logic above, forgot to mention the usage of <div v-if="friend.session && friend.session.open && atLeastOneOpened"> before, maybe that is the problem?

Nakov's avatar

Does your collection contains empty items? And is each item an object?

kendrick's avatar

@nakov Thank you for the patience - It returns the following, when I search for a user I know exists:

Object
data: Array (1)
0 {id: 2 ...}

otherwise:

{data: []}

Both ways, resulting in showing no users.

Collection could contain empty items, as I am merging different query builders to $users?

public function search(Request $request) {

        $user = auth()->user();
        $friends = $user->friends()->where('first_name','LIKE','%'.$request->search."%")->get();
        $painters = $user->painters()->whereHas('user', function (Builder $query) use ($request) {
    $query->where('first_name','LIKE','%'.$request->search."%");
})->get()->map->user;
        $collection = $friends->merge($painters);   

        if ($request->ajax()) {
            $users = $collection;
            return UserResource::collection($users); 
        }
    }

Nakov's avatar

Steps that I will try is delete the lines that I doubt that don't work while doing a search and putting them back one by one until I find what causes the problem exactly.

Check the response from both getUsers and doSearch and make sure that the data returned has the same structure and same data type ie, array of objects.

Don't know how else to help here.

kendrick's avatar

@nakov thank you - already started with that:

If I console.log within the getUsers() method:

this.users = res.data.data;
console.log(this.users); 

it returns a different structure:

[Object] (1)
Array (1)
0 {__ob__: Observer, …}
first_name: 

Structure after a doSearch:

{data: Array}

If I add:

<div v-for="friend in users">
   <p>test</p>
</div>

above the actual loop - it also hides test, if I trigger the search. Data structure remains the same.

kendrick's avatar

@nakov - Sorry for coming back.

In my vue DevTools, if I trigger a doSearch users will turn to:

users: undefined

That is obviously the problem, but why?

Maybe because in getUsers() I am triggering this.users = res.data.data;, whereas in doSearch I am triggering self.users = data.users;?

Nakov's avatar

@splendidkeen that's why I said you should log the results in order to ensure that you get same data type back..

try this :

$.ajax({
    context: this,
    type : 'get',
    url : "/search",
    data:{ 'search': this.search },
    success: function (data) {
        console.log(data);
        this.users = data.users;
    }
});

Using context here.

And make sure that the browser console spits out an array of objects.

kendrick's avatar

@nakov - What I get in the Chrome console (due to console.log(data);) is:

{data: Array(1)}
data: Array(1)

but users: undefined remains in vue Devtools and the users will be hidden, that is so crazy!

Nakov's avatar
Nakov
Best Answer
Level 73

@splendidkeen no it is not crazy.. You are getting data with Array of data, and data is not an object in your case which has a users key.

Does just this do the trick:

this.users = data;

instead of data.users

kendrick's avatar

@nakov - that has done the trick, thank you for your patience - users contain an object now, after search. But I still get a [Vue warn]: Error in render: "TypeError: this.users.some is not a function" which will not allow the users loop to work correctly.

Could this be solved with an optional logic on the atLeastOneOpened computed method?

Nakov's avatar

@splendidkeen try putting a console.log within the computed method to make sure that always the result of this.users is an array as some is a function on an Array in Javascript. If it somehow happens to not be, then add a guard based on the result that you get, to avoid calling some on undefined for example, in case this.users can be undefined at some point. Or make sure that you have initialized it to an [] array at least.

kendrick's avatar

@nakov - console log on the computed method results in:

atLeastOneOpened() {
      console.log(this.users);
      return this.users !== undefined && this.users.some(friend => friend.session && friend.session.open);
},

(4) [{…}, {…}, {…}, {…}, __ob__: Observer]
0: {…}
1: {…}
2: {…}
...

When I search I see [Vue warn]: Error in render: "TypeError: this.users.some is not a function"

Nakov's avatar

@splendidkeen Unfortunately I don't have your project to look on this for you, you will have to find why it returns an array in some cases and an Object in another case. Because clearly you get the error when you response from the controller is an Object instead of an Array like the getUsers returns.

So once again make sure that you return the same response both times. I am out now my friend, sorry but I have work to do.

1 like

Please or to participate in this conversation.