Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Filtering the Products

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Ok team! We have the currentCategoryId and we're using it on the sidebar to highlight which page we're on. That is awesome! But our products on the right aren't being filtered at all yet! Yikes!

Go to /api to check out our API docs and scroll down to the GET /api/products endpoint. I've already done a bit of work behind the scenes in API platform to allow us to fetch all the products for a specific category.

Here's how it works: we can enter a category's IRI into this box: so, /api/categories/ - and then 24 to get the Furniture category. When we hit Execute... yes! It only returned 5 items! And if I scroll up, you can see the URL that gave us this result: if you ignore the url-encoded parts, this is /api/products?category=/api/categories/24.

All we need to do is make that same request from inside of Vue!

Passing currentCategoryId as a Prop

Head back to our app. From the top level products.vue, hold Command or Ctrl and click <catalog /> to jump into that component. Catalog is responsible for loading our products on created by making a request to /api/products. Hmm... I wish we had access to the currentCategoryId here... because we could use that to change the URL in the Ajax call! Well then... let's go get it!

Back in products.vue, add :current-category-id="currentCategoryId" to pass it as a prop... just like we did with the sidebar.

<template>
... lines 2 - 11
<div :class="contentClass">
<catalog :current-category-id="currentCategoryId" />
</div>
... lines 15 - 16
</template>
... lines 18 - 53

Now, in catalog.vue, add that as a prop. Actually, let's go steal the prop definition from sidebar - it's perfect there - and paste it here.

... lines 1 - 18
<script>
... lines 20 - 23
export default {
... lines 25 - 29
props: {
currentCategoryId: {
type: String,
default: null,
},
},
... lines 36 - 53
};
</script>

Using currentCategoryId to Filter on the Ajax call

Wonderful! We are now receiving currentCategoryId. To use this, down in created we could add ?category= and then the currentCategoryId. But with Axios, there's a better way. Create a new params variable set to an object: this will hold all the query parameters that we want to send. Now, if (this.currentCategoryId) {, then params.category = this.currentCategoryId.

... lines 1 - 23
export default {
... lines 25 - 41
async created() {
const params = {};
if (this.currentCategoryId) {
params.category = this.currentCategoryId;
}
... lines 47 - 52
},
};
... lines 55 - 56

To pass that to axios, add a second parameter, which is an options object. One of the options you can pass is called params. So: params: params,.

... lines 1 - 41
async created() {
... lines 43 - 47
const response = await axios.get('/api/products', {
params: params,
});
... lines 51 - 52
},
... lines 54 - 56

Object Shorthand: Keys without the Key

That should work... but yikes! ESLint is mad! It says:

expected property shorthand.

This is referring to something that we've actually already done many times, but I want to highlight it in case you haven't noticed. In JavaScript, if you are trying to add a property to an object... and that property's name is the same as the variable being used for its value, you can use a shorthand syntax: just params. This sets a property named params to the params variable.

Check it out!

If we go over now, you can already see it! It reloaded! Yes! Furniture shows furniture! Breakroom shows breakroom products! Office supplies shows office supplies! And Snacks shows... uh... did this just break? Where are my snacks?

It turns out that our snacks category is currently empty! Gasp! If that's not bad enough, instead of saying - "No snacks! You're on your own!" - we see the loading screen forever.

That's... my fault. When catalog renders product-list - hold Command or Ctrl and click to jump to that - the loading is showing based on whether the products length is zero or not. An empty category looks like it's still loading!

We need to improve this: we need to truly know whether or not the Ajax call has finished! Let's make a smarter loading mechanism next!

Leave a comment!

10
Login or Register to join the conversation
davidmintz Avatar
davidmintz Avatar davidmintz | posted 2 months ago | edited

I hate to be a whiner, but I am stuck with a weird problem I can't figure out. My products are not being filtered. I keep getting 12 items of different categories.

For example: When I hit the url https://127.0.0.1:8000/api/products?currentCategoryId=/api/categories/2 using the Platform API interface or curl, the data comes back as it should: five items of office furniture (the id of the Office Furniture category in my world being 2 rather than 24). When I click Furniture in the sidebar category, however, I get the 12 unfiltered items.

Doing my Troubleshooting 101, I ask, OK, is currentCategoryId set correctly by Javascript when the page is rendered, or is it null? It is correct. Are the right params being passed in the axios call? Yes. What is the URL we are hitting with this axios call? It is indeed https://127.0.0.1:8000/api/products?currentCategoryId=/api/categories/2 according to the Firefox console. And what data is in the response? The 12 unfiltered items. And that is what is being assigned to this.products in my created() method.

Any ideas?

async created() {
        console.log('"created" is running');
        const params = {};
        if (this.currentCategoryId) {
            params.currentCategoryId = this.currentCategoryId;
        }
        const response = await axios.get('/api/products', { params });
        console.log(`our category id is ${this.currentCategoryId}`); // output: our category id is /api/categories/2
        console.log(params); // output: Object { currentCategoryId: "/api/categories/2" }
        this.products = response.data['hydra:member'];
        console.log(response.data['hydra:member']); // 12 items
    },

    ```
Reply

Hey David!

A mystery! Ok, so we know that going to https://127.0.0.1:8000/api/products?currentCategoryId=/api/categories/2 via the (1) API Platform interface works correctly but (2) going to this URL via axios is NOT working correctly. That IS, indeed, super strange. What could be different?

The only thing that immediately came to mind is that the API Platform interface is setting an Accept header on the request - iirc it's application/ld+json. However, I don't think that would make a difference in how the items are filtered... and I think API Platform (via axios) IS returning json ld anyways. So, I don't think this is it.

But something must be different. So here's what I would do:

A) You are already able to get a "curl" command version that DOES do the filtering correctly. Awesome!
B) After making the Ajax request via Axios (where the filtering is incorrect), in your network tools, you should be able to right click on that request and select "copy as curl".

Cool! So now you have 2 similar (but not identical) curl commands: one that works correctly and one that doesn't. Try tweaking each of these until you find what subtle difference is causing the bad behavior. I'd love to know what you find out.

Cheers!

Reply
davidmintz Avatar
davidmintz Avatar davidmintz | weaverryan | posted 2 months ago | edited

OMFG. You won't -- no I think maybe you will! -- believe this. I was saying
params.currentCategoryId = this.currentCategoryId
and the attentive reader will see there's a wee little mistake there. The name of the parameter should be category. This is what I get for typing like a good person rather than copy-pasting (-: D'oh. So embarrassing, maybe we should delete this little thread.

Thanks for putting me on track to figuring it out!

Reply
Thibaut Avatar

Hello!

I love this course, great job as always.

I was just wondering if it was considered "best practice" to keep using props to pass an information we're collecting with a service.
The reason why I'm asking is because I was doing this part of the course on my own (for the challenge - and being really psyched about Vue! :p) and I stumbled upon that reflection: should I keep passing this piece of basic information (routing) through props from component to component or should I just call the service directly from the component I'm currently using that information?
My answer was to import the getCurrentCategoryId() method from the page-context service but I'm very interested in knowing your take on this.

Reply

Hey Thibaut,

That's a good question, and the answer is (as always) that it depends on your app. If the service method you need to call tends to change or has arguments that are not too easy to get, then it's better just to inject the value as a prop in your component. So, you need to evaluate what option will make your code more resilient to change

Cheers!

Reply

Hi!!

I am interested how should look axios get (what is sintax) if we use

https://api-platform.com/do...

How I can get all objects from db on curent date, or how I can get objects beetwen two dates

In my api platform everything is work, but I dont know how to send date (dates) via axios so I can filter result by date or range date.

Reply

Hey sasa1007

Have you tried sending your dates as a string? Then in your backend you can read the string and transform it back into a DateTime object

Cheers!

Reply

Thank you for your answer.

Uh, I'll try to explain.

I can get string from date object in my vue component and I can try to use it in axios, but I need to send somehow date (string) as parameter so I can get collections of object.

In my api documatacion when I put in entity this line of code

@ApiFilter(DateFilter::class, properties={"created"})

I get this

https://prnt.sc/uagsbt

When i put 02-09-2020 in field created[strictly_after]

my curl is look like this

curl -X GET "http://localhost:8000/api/presences?created%5Bstrictly_after%5D=02-09-2020&page=1"

so my axios call will bi something like this

axios.get('http://localhost:8000/api/presences'.+'?created[strictly_after]'=02-09-2020' ) which I dont like it and I thing that this sintax 'created[strictly_after]' is not going to work, and I hope so that there is a better way to assign a value to the parameters from screenshot via axios.

Best regards

Reply

I find some solution

axios.get(this.URL+'?created[after]=2020-05-15')

Reply

Hi Sasa, I think you are on the right path. Though I'm not an expert on axios, it seems you have to send that URL in order to get that filter!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This course is also built to work with Vue 3!

What JavaScript libraries does this tutorial use?

// package.json
{
    "devDependencies": {
        "@symfony/webpack-encore": "^0.30.0", // 0.30.2
        "axios": "^0.19.2", // 0.19.2
        "bootstrap": "^4.4.1", // 4.5.0
        "core-js": "^3.0.0", // 3.6.5
        "eslint": "^6.7.2", // 6.8.0
        "eslint-config-airbnb-base": "^14.0.0", // 14.1.0
        "eslint-plugin-import": "^2.19.1", // 2.20.2
        "eslint-plugin-vue": "^6.0.1", // 6.2.2
        "regenerator-runtime": "^0.13.2", // 0.13.5
        "sass": "^1.29.0", // 1.29.0
        "sass-loader": "^8.0.0", // 8.0.2
        "vue": "^2.6.11", // 2.6.11
        "vue-loader": "^15.9.1", // 15.9.2
        "vue-template-compiler": "^2.6.11", // 2.6.11
        "webpack-notifier": "^1.6.0" // 1.8.0
    }
}