Component Building with Quasar
Or with Vue for that matter.
--
Note to begin with: This is written with Quasar version 0.17. Since then, Quasar v1.0 as been released. But, the concepts are still valid, just the code here might not be. đ
tl;dr; #1â If you already understand the basics of composing a UI in Quasar or Vue and just want to see some of it in action with Quasarâs QBtn component, drop down to âReasons for Change â SRP in components?â
tl;dr; #2 â If you donât know about backend templating engines or donât care about inheritance of template code compared to composition, drop down to âAn example of component building with QBtnâ.
tl;dr; #3 â If you just want to play with some code from this tutorial, check out the Sandbox demo (give the sandbox time to fire up). đ
Before we begin, this article is going to assume you have some basic knowledge about how Vue works. If not, you might get a bit confused. If you are feeling confused after reading this article, you need to learn more about Vue. And please donât be frustrated. If you donât know React or reactive UI technologies, take a step back and learn what they are about. Then come back. You wonât be disappointed!
Ok. A long lead in that was⌠letâs get going.
The whole concept of Vue is the composition (as opposed to inheritance) of components. If youâve come from a backend language, where youâve used some sort of templating engine like Smarty, Twig, Blade, erubis, hero, Velocity (and the list goes on and on), then you probably already feel at home with Vueâs <template>
syntax and Single File Components. However, we need to understand this is still a different system compared to backend templating engines and their âway of thinkingâ.
Those back-end templating engines all have a top-down methodology with the capability to inherit template code. You build a page and try to think about what needs to be extracted to reduce code duplication and you try to use inheritance to extend those smaller templates, but only where their features differ slightly.
With Vue, the goal is to think of components first, i.e. the smallest reusable parts of an application, and build up from there.
Also with Vue, and this point is the most important one, we are building the model inside these components. Whereas, in the world of server-side templating engines, we inject the model, which we built in some other part of the program. With server-side templating engines, weâre usually using an MVC architecture.
In Vue, the architecture is not MVC but rather MVVM architecture. We are building the model within each component, but only for that component.
If we start to make a complicated component in Vue or Quasar, our model gets complicated too. With composition, we can and should avoid that complication.
Iâm going to put something out there that might be controversial. Any Vue component template should not be verbose! If you have more than 40 to 50 lines of template code, then your template or rather your component, is doing too much. This might expand to 100 lines or so, if you are using render functions. At any rate, if youâve heard it before with MVC architecture, âthin controllers, thick modelsâ, then I say something similar goes for the MVVM and SFC architecture.
ItâsâŚ..
You should have thin structures and thick models.
Theoretically, with thin structures, i.e. small templates, we donât need the inheritance of template code between components. Still, inheritance of template code between components makes for a heated topic of discussion in Vue circles. Itâs been suggested more than a couple of times to get template extension through inheritance in Vue, like in this issue. And every time a suggestion like that comes up Evan You, the creator of Vue, doesnât bite. He says an additional extension system for just the template syntax would make Vueâs current system unnecessarily harder to reason about. I agree.
Composition for the win! đ
Code reuse with Vueâs composition techniques (i.e. the slot system, mixins, component elements/objects, etc.) do seem just a bit laborious at times. And some of it, like slots, is so different to what was previously known, it catches beginners to Vue a bit off guard. I was in this boat too.
Nonetheless, if you think about what you want to get accomplished and then âbuild outâ or compose your version of components, understanding the DOM hierarchy rules (Parent / Child relationships) and keeping SRP and âthin structure/ thick modelsâ in mind, that is all you really need to achieve code reuse (which is the goal of inheritance too). The DRY principle of programming, i.e. âcode smartly once and reuse it oftenâ, can be relatively easily achieved with Vueâs composition facilities.
The aim of this tutorial is to get you thinking (more) along those lines.
An example of component building with QBtn
Iâm repeating this again, because it is vital to understanding how to work with both Quasar and Vue. Vueâs core premise is built on composition, which means the action of putting things together. These âthingsâ in Vue are components. Check out the meaning of the word âcomponentâ.
Itâs Latin for âPutting togetherâ đ
This may be a no-brainer but, it is easy to start building components that arenât components. They are more like in the world of OOP, god components, doing a lot of stuff and breaking easily. It can happen quickly and why I keep mentioning the Single Responsibility Principle (SRP). Take advantage of Vueâs composition facilities and remember SRPâŚâŚalways!
As you know, Vue takes your components, your compositions and smartly puts them in a virtual DOM hierarchy, then renders that to your users and makes it all reactive. Itâs why we canât âunthinkâ hierarchies with components, because the DOM is a hierarchy. And this hierarchy doesnât matter for interactivity with the user, because when components are done right, the data manipulations, the bindings, are directly visible within the DOM, at the right place at the right time. This âreactiveâ system is what makes reactive UI libraries like Vue worldâs better than you having to find the data in the DOM to then manipulate it (ehem, like in jQuery).
Remember the other core concept of Vue and reactive programming isâŚ
âLet your data do the walking!â
đ Ok. I made that upâŚâŚ. But, itâs true! đ
Reasons for Change â SRP in Single File Components?
There are five possible âresponsibilitiesâ or reasons why we may be touching a component to change it. Now you might be saying, um, but the âSâ in SRP is for âSingleâ. Yup, and theoretically, we can separate these 5 responsibilities into their own modules/ files for change. More importantly, a Vue component can hold them all and yet still separates them beautifully. These reasons for change or responsibilities are also the sources as to why we would want to âcomposeâ them differently. They are:
- Styling/ Design (CSS)
- Structure (HTML)
- Behavior (JavaScript)
- the all important â Data Model or Model(also JavaScript)
- and the (hopefully)single goal of the component
If you are going, âum, say what?â right now. No worries. Hopefully, youâll get an understanding of what is meant above, as we go through the tutorial.
The Plan
Like with any type of creation, there needs to be a plan in place for our new button to end up as we intend it to be. You may think, âHey, itâs just a little olâ button.â Well. This tutorial will show you a custom button component in Quasar (or Vue) can be quite powerful and intricate. So, having a plan - an idea of what you want the button to do or be - is really important.
There are so many possibilities with just this one component that composing it to be something more with other Q-components or logic/ behavior, and even only using it as a stock button without having a plan, will most likely end up in a crazy, maybe even frustrating, mess.
The power of Vue lies in your easily keeping a mental model of what you want a component to do. However, that mental model also needs to be formulated before you begin âcomposingâ.
So, this is what our button should do, when it is finished (noting the top 4 âreasons for changeâ mentioned above in parenthesis). The plan itself is the 5th reason. Itâs our final objective. And yes, this example is a bit contrived, but letâs just have some fun with making new button components. đ
This is the plan for our button(s):
- The button should be able to have an image (styling/design).
- If there is no image, it will return to being a ânormalâ Quasar button automatically (behavior, styling/design).
- It should have a tooltip option, no matter what form it is in (regular or with an image) (structure, behavior).
- The tooltip will be controlled via data input only (data model). That means, if a tooltip text is in the data, a tooltip will show up, automatically.
- The tooltip will either be at the top of the button or at the bottom (styling/ design).
- Weâll also offset the tooltip with 5 pixels above or below the button as standard and fixed, simply because we think it looks better (styling/ design).
- From the Quasar Btn component, our button should have a (styling/design):
- Label â for text in the button.
- Color â for coloring the button.
- Icon â to add an icon.
- Size â to control the size of the button ,when it is a normal button. Weâll only use the pixel values for now for the image type button.
- Flat â for a flat button.
- Round â for round buttons (think about avatars, where you need a link to a profile, etc.).
- Outline â for adding an outline.
With the QBtn props, we could go on and on. There are 29! However, weâll stop here. Your decisions for your new component will determine the props to take over. - Additionally to the Quasar Btn props, our button will have:
- Outline Color â since the Quasar Btn has only one color and we are using that currently for our background, we need a different prop for the image buttonâs border color.
- Image URL â for optionally adding an image for our image button (structure).
- Tooltip Text â if our button should have a tooltip (behavior, data model).
- Tooltip Location â either âtopâ or âbottomâ. âTopâ is the default. Yeah, we are reducing the feature set of QTooltip, but for our use case with buttons, we only want this behavior.
- Visibility Control â to show only when it should be shown (behavior , see point 10). - We know our image buttons will always be flat and should be outlined in the color selected in the color prop(styling/design). The text is the same color too (styling/design). This is simply how QBtn works.
- The button should have a visibility prop. If it is true, the button is visible. If false, it is not there (data model, behavior).
- As a last objective and not part of our button composition, weâll put together a âbutton barâ demonstrating the compositional ability of the âslotâ in Vue.
You must admit, that is a lot of things to consider. We are making a âsuper duper power buttonâ. đ And, maybe we will want to have button components that only do some of these things, right? Or, well, maybe not. No matter. Weâll build out the different buttons for the sake of demonstrating composition.
So, with that in mind, letâs compose 4 different buttons and our button bar.
Button #1 â Styling/ Design with an Image
Please note that the /components
directory is our own component code and our /pages
directory is where the components are consumed and demonstrated in this example.
Ok. That saidâŚ.
Although we can argue about this, adding an image to a button is a design/ styling decision. Think of the places we would like to make an image âbutton-likeâ in a website. How about those avatar buttons you see so often in chats or forums, which link to a profile page, or even offer a context menu? Iâm sure your imagination is filling up with ideas, right?
Have a look at the btn-image.vue
component in the components section of our sandbox (Give the sandbox some time to fire up. Itâs also better to launch the app in a separate browser.). This is a button element with an image.
If you look into the model of the component, youâll notice weâre high-jacking some q-btn functionality. Note: this component is NOT finished!
If you also look at the model (the code in the <script>
tags), you can see where weâve added our props.
There are naturally a ton of other considerations we could build into this button, like linking to external pages, and there are even some bugs. If you feel adventurous, you can take the code and improve it. For this little tutorial, itâs current condition does the job.
A remark on the side. Iâm a lousy front-end developer, which this component shows. As I was testing, I found issues with my original button. And, I knew the âreasons for changeâ and they brought me right smack back to this one component. In other words, my reasons to change it (remember there are up to 5 per component) caused me to go straight to this componentâs code. This is SRP for Vue/ web components in action!
Button #2 â Behavior #1 - With no Image, it will be a Normal Button
Letâs build on what we just built with button #1. If there is no image, we want to show a normal Quasar button.
Have a look at the second button example, BtnOrBtnImage
, in our sandbox.
You will probably say, the purpose of this button is sort of nonsense, but humor me for compositionâs sake. đ And notice, weâre only adding one feature. The button swapping. Remember SRP?
Using our first Mixin
For the creation of this button and the reuse of btn-image
weâve also created a mixin called btn-image-mixin
. Have a look at that mixin in the sandbox.
Weâre importing the first component we made, and the mixin for itâs properties (which includes props and a method, but can also contain lifecycle hooks, data, basically anything defining a component). Youâll notice too that the model section is now much cleaner. In fact, this component is rather small, which is a good thing!
Also important to note, now that we have the mixin, we could refactor our btn-image
component to use it. But, weâll keep it as is for now.
Button #3 â Structure â adding Tooltip Functionality
By structure, we mean adding something to the componentâs HTML syntax in order to change what is going to be rendered to the user.
We simply canât avoid HTML, as itâs what the browser requires to build the DOM. In Vue, we have the <template>
(or the render function) sections of the component to hold the structure. It helps âseparateâ the responsibility of structure from the rest of the component code. Once again, we need to remember that there are no facilities in Vue to extend template syntax.
Currently, QBtn already does a whole lot for us. We can make a button link to other parts of our app (which could be seen as adding structure and/ or behavior). We can add different icons (which could be seen as styling). We can control the text showing in the button (which could be seen as model changes), color (styling), and a whole lot more.
QBtn is basically giving us multiple possibilities to manipulate the button, according to our usage needs (and according to the data/ state we give it).
But, letâs just say for our exampleâs sake, we also want to add a tool tip on a button as a standard âaccessoryâ or feature.
For this, we want to create a new component called btn-tooltip
, which stands for âButton with Tooltipâ, of course. đ
As you can see, weâve simply added the tool tip to our btn-or-btn-image
component. We also have the disable feature from QTooltip in use, in order to show or hide the tooltip according to the data given, i.e. a tooltip text, which is our added prop.
Notice we are using computed properties, in order to control the disable prop and to control the location of the tooltip. The computed properties give our data (or the lack thereof) the power to show or hide the tooltip or put the tooltip at the top or the bottom of the button. Also notice the new mixin. Iâll leave the rest for you to investigate. đ
And lastly with the consumption of this button in the button3
page, weâve brought in the v-bind
directive. This is a powerful little directive, which allows you to define all your props in your data, saving time by only writing (or automating) the props for your template once.
v-bind
also allows for an easy and dynamic manipulation of prop data. Itâs a win-win directive. đ
Button #4 â Behavior #2â adding a Visibility Control
Another feature of our button, and the last one, is going to be a visibility control. When this control is on or true, the button shows itself. When it is off, the button goes away. Think about the UX, where certain users, like admins or owners of a record, get more functionality on a record than do guests or normal users. Because they are more privileged, they get extra buttons to do more with the data. Think of how often that is needed everywhere in an application, especially when Create, Update or Delete activity is available for some users, but not others.
Notice weâve called the button âBtnMainâ. All the naming might be questionable throughout this tutorial and as you hopefully also know, proper naming is hard to do in programming. The intention with the name âBtnMainâ is to indicate it is the âtopâ of the button hierarchy and functionality. It means it is the most feature packed button. And theoretically, our set of new buttons could be in its own directory. The âmainâ name would lead you to that button as the top one to get all features at once (if you need them).
To allow for this control of visibility, we simply added âv-ifâ to make this feature work, along with a new prop visible.
Thatâs it. But now, when we consume this button in our higher components, all we need to do is define a Boolean data property, or create a computed property or watcher, to âsignalâ the button to show or not.
Thatâs it for the features we wanted to add. Hopefully youâve also noticed, weâve done only one feature per iteration of new button. It may seem a bit repetitive, but it allows for SRP. In other words, say you only want a button with an image, then you only use that one button.
A Button Bar â bringing it all together with slots.
Now weâre going to bring in the other and most powerful component building tool we have at our disposal in Vue and of course, in Quasar.
The âSlotâ.
If youâve worked with Quasar some, youâll notice that slots are âgiven to youâ in a lot of places. I say âgiven to youâ, because the whole idea of the slot is for you to be able to âinjectâ your own template code, as youâd like it, into the componentâs slot.
Another way to look at a slot is like an API for a component, which helps us avoid that pesky want to extend template code. Yes, we must build out the slot template (which can be an HTML element too, not necessarily a template element or even a component) to make it work, and yes, that might mean extra components or HTML code, depending on what we want. But remember, we only need to build them once AND we can use our compositional methods we practiced earlier to simplify that building (or even automate it with our data! Oh yeah! đ).
Am I confusing you? Sorry. It took me some time to grasp the magnificent power, yet simplicity of Vue too. I hope this last demonstration will open your eyes (if they arenât already. đ)
For our final exercise, we are going to create a âButton Barâ. Think about how often you see where there are a set of buttons within your application. You might have a set of buttons at the top right of your application. You might have a set of buttons at the bottom of dialogs. You might have a set of buttons in the footer of a record page to do data manipulation. The list can go on and on. Weâll have a look at the use case of the usual application buttons at the top right of a community application page, like in a forum or chat. Here is our btn-bar
component code.
As you can see, we simply have a <div>
, which holds a loop to go through the button prop, which will be an array of buttons. We also have a slot for the button. Simple.
In our page to demonstrate the btn-bar
component, we have our btn-main
to fill that button slot. Notice we are also creating the buttons, with the inner template.
Amazingly simple, right? Ok. From a conceptual perspective, you might still be going âWTF?â. Slots are tricky to understand, but once you do, they are really powerful and solve a bunch of use cases. Have a good look at this usage of slots. Itâs actually a simple use case.
We can also consume the buttons singularly and as different components by using a âgeneralâ component
tag and using :is
to call the right button component, instead of using the btn-main
component. The :is
feature on the general component
tag is another fantastic tool for logically selecting component types. Below is what the component could look like, if we wanted to use different button components, defined through our data.
I hope that didnât confuse you more. If it did, try to work with slots yourself. Here is also a fiddle to help to understand them. It is a simple composition of âquestion componentsâ for a survey.
The Data âControlling your Vue, ehem View!
Jup. Iâm often incorrectly writing âvueâ for âviewâ these days. đ
If youâve been following closely up till now, youâve noticed that our toggling actions were only changing the data. That is the power of reactive systems/ UIs.
Take a look at the data models and what we are toggling through. Each toggle changes the data model, which controls our button variations or how they are presented to the user. This is getting into the realm of meta programming, where data models form our UI experience and not necessarily the code we are writing. It is quite powerful, if done right, because it can give our users the power to make decisions on our applicationâs UI and how it works (if you are into SaaS and multi-tenancy, you know what I am getting at). Our example button with added features are somewhat a bit contrived, as mentioned earlier too. However, our goal is to demonstrate composition.
As a last note, notice how our final btn-main
component actually doesnât need much markup. This is important with Vue and Quasar and why you should avoid a lot of markup on your templates. KISS them and rememberâŚ.
Thin Structure, Thick Models! đ
Last, but not least.
There are other tools within Vue that need mentioning for compositional work.
The âFilterâ
Filters are ways to manipulate text output. They were used much more extensively in v1 of Vue, with that version having a good number of built in filters. For v2, filters were reduced a lot, simply because computed properties achieves the same thing, but with better performance. An example of a filter would be to always capitalize the first letter of a text.
And this is how it would be used.
Notice the â|â (pipe) character between the text variable and the capitalize filter. Thatâs how you use filters. You can also use them in v-model attributes too.
The âextendâ method of the Vue object.
This method, in essence, is the same as a mixin. It takes the component object model and merges it into the calling component. Its name is a bit misleading. Please do not think in terms of inheritance, when using extend. I think this âmisnamingâ is also why the âextendâ method/ feature isnât at all documented in the Vue Userâs Guide. Iâve also heard Vue core devs say, just avoid using âextendâ and use mixins, simply because the mixin methodology is easier to reason about. So, just do that please. đ
The âprovide/ injectâ options.
I wonât get into this part of Vue much at all, because it is not meant for normal application code. The Vue docs mention this as a clear warning. Iâll just quote that warning.
provide
andinject
are primarily provided for advanced plugin / component library use cases. It is NOT recommended to use them in generic application code.
âprovideâ and âinjectâ offer the plug-in author a means to share data or props among components deeper in the component hierarchy. Again, itâs mainly only for plugin usage. So, unless you are going to be writing a plug-in for Vue (or Quasar), donât worry too much about âprovideâ and âinjectâ for normal application programming.
The directive.
The directive is also a code reduction tool and helper, when it comes to component composition.
In this tutorial, we were mainly concerned with the composition of âwhole componentsâ, so we didnât cover directives in any depth. Quasar solves a lot of directive concerns for you, and I mean a lot of them. Not only are there a number of awesome pre-defined directives, but there are also a lot of standard behaviors built into the components themselves. Think about all the âonâ events given to you out of the box in each component. They arenât directives per se in Quasar, but they could be.
In fact, we used one of the directives in our image button example. The âripple effectâ.
Directives offer a means to build global effects or behaviors, which can be used on any component, like the ripple in Quasar.
So in the future, if you have a common behavior among components, which Quasar doesnât offer out of the box, you can single out that behavior and code it as a directive and just add it as an attribute to your template elements, where needed. If you want to learn more about creating directives, have a look at this video.
Fin!
There you have it. With just a couple of compositional tools, you have a lot of power to âbuild upâ your UI. Again, think smallest thing to biggest âthingâ, when creating components. And remember Vue and Quasar follow the DOM hierarchy in âbuilding upâ those components. Donât start with a page, when thinking about your application. Start with the smallest elements of the page and think hard to see if even those elements can be broken down further. Build the components for them and either slot them in or compose them as they would be in the DOM hierarchy. That is all you need to do.
Oh and, donât forget.
Let your data do the walking! đ
If you need more information about Quasar itself, here are a few links for your consideration:
COMPONENTS: https://github.com/quasarframework/quasar
CLI: https://github.com/quasarframework/quasar-cli
THE DOCS: https://quasar-framework.org/
DISCORD: http://chat.quasar-framework.org/
FORUM: https://forum.quasar-framework.org/
TWITTER: https://twitter.com/quasarframework
STEEM: http://steemit.com/@quasarframework