Planning Tabs API0:30
If we have something like that, then we'll do another one, hello world again, and then one last one. Okay, but next we need to figure out how we do the titles. So this would be the actual tab heading for each. I think we'll do a simple prop here. We could do heading or title. So I'll just do first, second, and then third. Okay, so at least to start, this is how we want it to work. Alright, so it sounds like we need two global components. One for tabs, and one for tab.
Creating Tab Components1:01
Alright, so it sounds like we need two global components. One for Tabs, and one for Tab. Alright, I'll switch back to our entry point. This is where we have all of the components from the whole series, and we'll set up a new one. Tabs and Tab, and then finally I'll duplicate this for Tabs and Tab. Okay. The next step is to create those files, resources/js/components/Tabs, and I'll paste in a little snippet there, and then we'll have another one for a single Tab. Alright, so we basically have a TabContainer.
little snippet there, and then we'll have another one for a single tab. Alright, so we basically have a tab container. You can even call that if you want, and then we have each individual tab. And as we discussed, each individual tab to start should have a title. Now we know, if I close this out and switch back, that our default slots will need to be right there. Okay. So I can't do this, you may know this, and that's because the slot content could be multiple top-level elements. So we always want to wrap it in a single root element.
Rendering Tab Headings2:57
So now let's switch back to our container here. And yeah, as I see it, we're going to need the tab headings, right? But below it, we'll also need our main tabs. So take a look at this. Right here, all of these tabs will be inserted right there. So now at this point, you can imagine it'll be something like foreach tab, and I'll probably need the index as well, for each tab in some tabs data property. So we'll need to figure out how to fetch all of the tabs that have been declared here. It's a little bit tricky. But for each one, we will render a button.
It's a little bit tricky. But for each one, we will render a button. And we'll say for the button, the text of the button will be the tab's title. So once again, if we switch back, each tab has a title prop, right? So we're going to spit that out within the button. Next, we have other things here, like we can set the role to tab. And then we need to handle what happens when you click on this button. Well, if you click on the heading, it's like we want to update what the activeTab is, right? So maybe we'll have something like activeTab, and we'll make that equal to the current.
Accessing Child Tabs4:35
So this is a little different from some components you'll work on. Take a look at this. When the component is mounted, I'm going to console.log the VM, and we'll take a look. All right, so if I give this a refresh, here is our tabs component. And you will notice this children property here. And if we take a look, those are the three tab child components. So we might start there. If I switch back, I could say I want to have access to those. So I could say this.tabs equals this.children. Okay, so we give it a refresh.
So I could say this.tabs equals this.children. Okay, so we give it a refresh. I switch over to view dev tools, and now I have access to all of those child elements, or view components. And sure enough, if I close this out, I can see the headings. But what about the body for each heading? Well, temporarily, let's update this back to true, and we should now see it. All right, so now we have our headings and the body for each. So why don't we take a minute and style this, and then it'll be a little more easy to take in.
So why don't we take a minute and style this, and then it'll be a little more easy to take in. We're using Tailwind again, so I will add some classs, like flex. We should add a little margin-bottom below. We should have a border. But actually, you know what, just a border on the bottom. And then let's set the color to, I don't know, 400. That's fine. Okay, so that will take care of our container there. Okay, next, if I switch back, we need to style either the button or the list item.
Managing Active Tab6:28
We know this isn't quite right, because these stylings will apply to all of them. But at least to get us started, you're beginning to see the end result there. Okay, so we'll come back and finish that in a moment. But I don't want to display the contents of every tab at the moment. So what we will do is come back, return this show property to false, which means we won't see anything. Now we have to determine, in our tabs container here, which one should be shown. Well, to start, why don't we say, we will allow the user to overwrite this, but to start we'll say the very first tab is the active one. So I could do it programmatically here.
we'll say the very first tab is the active one. So I could do it programmatically here. Okay, so now if I come back and refresh, we only see the first one. But there's still no indication that this is the active tab. So why don't we do this instead? Rather than overwriting a specific tab, I'm going to set the activeTab equal to the first one. Then, we could have a watcher here, where we say, okay, keep an eye on that activeTab property. And when it's changed, we will update this property for each of the children.
property. And when it's changed, we will update this property for each of the children. We could do that by saying, okay, map over all of the tabs, and let's see, let's not overwrite this. map over each of the tabs, and we're going to set the tabs show property equal to whether or not the current tab is the active tab. Okay, so we're just looping over all of the children, and we're updating its show property. And when we do this, every single tab will have its show property set to false, except for the one that is the actively selected tab. So now, take a look at this.
for the one that is the actively selected tab. So now, take a look at this. If we scroll back up to our button here, I could set a class here, and we'll say, alright, I'm going to add a class of, hmm, we'll start by saying font-bold, and that will be applied dependent upon whether or not the current tab is the active tab. Okay, let's try this. Give it a refresh, and there you go. Let's select another one. Now that's the active tab, and you see it's dynamically updated here. And the third, second, first, third.
Next up, what we have here is fine if that's the look you're going for, kind of a filing cabinet thing, but let's make it a little bit cleaner. I don't want to see this border unless it's the active tab. So once again, I'll add a class here, and we'll move all of the border stuff here down below. So apply all of those once again if the tab is the active tab. And we might even want to make that a little helper method or computed prop. Okay, so now come back and refresh, and only this one has the border. But next up, it's pretty common to hide this little border. And here's how we can do it.
This is just a simple explanation with no code.
Supporting Default Active Tab11:13
Okay. Just a couple more things. It would be nice if I could set a default tab. So right now, it's always the first, but maybe I want this one that says Woo to be the default tab. Okay. Maybe we could do something like this, active. And if I add that prop, this will be the one you see by default. All right. Well, it sounds like we need to accept active, but I do want to set its type.
All right. Well, it sounds like we need to accept active, but I do want to set its type. And that's because right here, I like setting active rather than this. So if I want to allow for that, I do have to set that it's a Boolean type. So I will rewrite this. Yes, we have a title, but then we have active, and its type is Boolean. And again, this is what will allow us to exclude the true or false value there. But then I will set a default value of false. Now if we switch back, we could say, hmm, how about right here, and I want to make this more clear.
Now if we switch back, we could say, hmm, how about right here, and I want to make this more clear. We're really saying set the initial active tab. It's a little wordy, but that's okay. Set initial active tab. All right. So now I can paste in what we had before. But even then, I'm going to change this. I'm not going to assume that it's the first tab. Instead, let's look into our children, and let's see if we can find a tab where the tab.active
I'm not going to assume that it's the first tab. Instead, let's look into our children, and let's see if we can find a tab where the tab.active status is set to true. So I could write it like that, or simply remove it. And then tab.active will return true or false. Now at this point, that will either return the tab that you applied active to, but what if you didn't do anything? Well in that case, this would return undefined. So let's say either return the tab that you explicitly marked as active, or we will do the first tab.
So let's say either return the tab that you explicitly marked as active, or we will do the first tab. Okay, and then we will assign that. So now we know when this component loads, which tab should be marked as the active one. So think about it. Let's go through the flow. When this component is mounted, we set the initial activeTab, and we calculate it. Then we have here, we're watching for that value to change. And when it does, we loop over all of the children tabs, and for each one, we update its display effectively.
So why don't we see if we can change this up a bit. And you have two choices. So often, your first instinct is to extract a method, something like isActiveTab, and then this will simply return whether or not the tab, basically the exact same check as we had earlier. Yeah, you could do this. So then, these references would change to isActiveTab. And then if I scroll down one more place here, I could say this.isActiveTab. And I think that should be fine. So if I give it a refresh, yeah, it still works.
And I think that should be fine. So if I give it a refresh, yeah, it still works. But instead, well, I often try to avoid this. isActiveTab, and then you give it a reference to the tab that you're inspecting. What if instead we just said, well, if the tab is showing or is active, then we're good to go, and there's nothing else to check. So if we wanted to take that approach, we could stick with show. We could add a second method. We could change this to isActive, really whatever you want there.
We could add a second method. We could change this to isActive, really whatever you want there. But if we kept it at show, we would adopt what the initial value was here, because remember, you can't mutate a prop. So this prop will set the initial value, and if we ever want to change that, we would then update the show value instead. Okay. So now, yeah, in this case, if you want to do isActive, that's fine. But if we don't change it, we would do tab.show. And I don't think that reads quite as nicely, which is why we might want to change it.
But if we don't change it, we would do tab.show. And I don't think that reads quite as nicely, which is why we might want to change it. So if we take that approach, this would become isActive, and then we would change it or toggle it here. And then finally, if I scroll down, let's update this value here. Okay. So now think about it. I could remove this method entirely from the parent, which is always good. And next, if we scroll up, rather than creating a new method, yeah, these would all just become props or methods on the child.
And next, if we scroll up, rather than creating a new method, yeah, these would all just become props or methods on the child. Okay. So let's, yeah, we have one more place. So here's where we keep an eye on what the currently selected tab is, and when that changes, we map over all of the children, and for each one, we update that field, equal to, and in this case, we can stick to our original check, and that's okay. Okay, so if I come back, refresh, it'll still work just as it did before, but I think that's a decent refactor. And that's basically it.
