If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeSo let's finally use the new sidebarCollapsed
data inside products
. Right now, when we collapse, we add a collapsed
class... which makes the element really small - just 70px.
Remove the .collapsed
class entirely: we're going to simplify.
... lines 1 - 68 | |
<style lang="scss" module> | |
@import '~styles/components/light-component'; | |
.component { | |
... lines 73 - 79 | |
} | |
</style> |
Inside the componentClass
computed property, copy the three classes that the sidebar always has. Then delete that completely. We're going to move all of the size logic to the parent component. Paste the three classes onto the outer div.
<template> | |
<div :class="[$style.component, 'p-3', 'mb-5']"> | |
... lines 3 - 39 | |
</div> | |
</template> | |
... lines 42 - 82 |
Over in products.vue
, the classes on the <aside>
and this <div>
now need to be dynamic based on the sidebarCollapsed
data. Hey! That's a perfect use-case for computed properties!
Down in our code, add a computed
option with two computed properties. Call the first asideClass
: it will determine the classes for the <aside>
element. Inside, return this.sidebarCollapsed
. If we are collapsed, use a class called aside-collapsed
- that doesn't exist yet, we'll create it in a minute. If we are not collapsed, use the normal col-xs-12
and col-3
.
... lines 1 - 17 | |
<script> | |
... lines 19 - 21 | |
export default { | |
... lines 23 - 32 | |
computed: { | |
asideClass() { | |
return this.sidebarCollapsed ? 'aside-collapsed' : 'col-xs-12 col-3'; | |
}, | |
... lines 37 - 39 | |
}, | |
... lines 41 - 45 | |
}; | |
</script> |
Oh, and Webpack is mad because, of course, this needs to be a function. That's better.
Copy asideClass()
, paste and call it contentClass()
. For this one, when we're collapsed, use col-xs-12 col-11
so that it takes up almost all of the space. And then when it's not collapsed, use the normal col-xs-12 col-9
so that it shares the space.
... lines 1 - 32 | |
computed: { | |
... lines 34 - 36 | |
contentClass() { | |
return this.sidebarCollapsed ? 'col-xs-12 col-11' : 'col-xs-12 col-9'; | |
}, | |
}, | |
... lines 41 - 48 |
Perfect! Well... no, not perfect: ESLint isn't happy about this computed
option:
The
computed
property should be above themethods
property.
This makes no difference technically, but there are some general best-practices about the order of your options. Let's move this above methods
to follow those.
... lines 1 - 21 | |
export default { | |
... lines 23 - 32 | |
computed: { | |
... lines 34 - 39 | |
}, | |
methods: { | |
... lines 42 - 44 | |
}, | |
}; | |
... lines 47 - 48 |
Ok: now that we have 2 keys under computed, we have two new variables inside of our template. Scroll up to the top. For the <aside>
element, we can say class="asideClass"
. Ah, but I'm sure you're starting to spot my mistake - we need :class
to make that dynamic.
Do the same for the div below: class="contentClass"
and then make it :class
.
<template> | |
... lines 2 - 3 | |
<aside :class="asideClass"> | |
... lines 5 - 8 | |
</aside> | |
... line 10 | |
<div :class="contentClass"> | |
... line 12 | |
</div> | |
... lines 14 - 15 | |
</template> | |
... lines 17 - 48 |
Sweet! Let's give it a try! I'll refresh just to be safe. And... ah! It works! It might feel smoother with some CSS transitions, but on a Vue level, this is working brilliantly!
Though, I could use a little more padding on the sidebar when it's collapsed... I don't want it all the way against the edge.
Go back to the computed property. When the sidebar is collapsed, it has an aside-collapsed
class... which I totally made up: that does not exist yet. To fix our padding issue, we could have said this.$style['aside-collapsed']
and then added a new .aside-collapsed
class to the style
tag of this component.
But... to make this more interesting, let's pretend that we're going to have multiple components across our app that will need to use this class. And so, I don't want to add it as a modular style to this component: I'd rather put this CSS in a central spot and share it.
That is why I used .aside-collapsed
instead of using the $style
variable. Open up scss/app.scss
and, at the bottom, add the style: aside-collapsed
with padding: 0 15px
.
... lines 1 - 56 | |
.aside-collapsed { | |
padding: 0 15px; | |
} |
Cool! When we move over to our browser... yes! It looks better already. So this is just a reminder that while modular styles are cool, if you want to re-use something, you can continue to use normal CSS files.
By the way, you could also use @import
to import CSS files from inside the style
tag of your component... you can even do it in a way that prevents the styles from getting the module prefix. To do that, add a second style
tag, leave off the module
and make the language css
. You can still import SASS files, but if you make the language scss
, for some reason, your CSS rules will get duplicated inside Webpack.
Next, it's finally time to make some Ajax calls and bring in some truly dynamic data!
Hey Nathan!
That's an interesting point of view. Extracting the UI Layout logic into its own code is actually a valid architectural decision. There isn't a single way of organizing an application, it all depends on your needs and the complexity of it!
The architectural decisions made in this tutorial have to do with the natural way of evolving in complexity and we follow simple rules such as: "dumb versus smart components" and "what is the deepest component that needs some data".
In fat, in general, in real world applications, the UI logic belong to components (not Vuex, for example) and they are kept as simple as possible, which is what we are doing here. :)
// 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
}
}
Hey ! Am I right if I'm saying : "SidebarCollapsed is just a state for the product page layout" ?
I feel like all the logic is about the product layout and not only the sidebar. So the SidebarCollapsed can be something like ProductLayoutCollapsed, and we can have another state with the SidebarExpanded for example, and a third state if we wish. With the naming SideBarCollapsed I feel like we are storing SideBar logic inside the product component and that break all the good vibes about splitting code into components. =D I mean, this is SideBar behavior inside the Product layout, but the button also change property on the content area. And maybe this button is going to be outside the sidebar ...
Just to be clear : I'm not saying the naming is bad. I'm just wondering if we can do even cleaner / sweeter code for the next developer going through this code. Don't want to be rude. Just a feeling that I want to share and discuss it =)