Designing components
I’ve long been a proponent of components. Be it web components, Polymer components or framework specific components (i.e. Angular, React, Vue, etc.). I believe it is the most efficient way to create an application and provide a high level of re-usability for specific needs. In this article I would like to show my thought process when creating a new component. I don’t really want to get into the code for the component but instead point out considerations that should be taken into account when creating a component.
While writing this article I kept struggling to come up with a good description while using an “any” component type of approach. I think it makes it easier to understand if I show how I approach a specific component instead. So, here goes…
Requirements
For discussion sake, let’s assume we need a component for managing email addresses. We want to be able to add and remove email addresses and each one be displayed as a pill or a tag with a little “x” on the right to delete the address. Simple, to the point and easy to remember.
Our initial requirements:
- A field to type an email address
- Pressing enter, space or comma turns the text to a tag/pill below the field, clears the field and ensures the field has focus for the next address
- Each tag/pill should have an “x” to the right of the text to delete the email address
- Clicking the tag/pill should allow you to edit the email address
- Pasting multiple email addresses might be supported time permitting
Implementation Concerns
Now, we need to ask ourselves some questions about how this component will be implemented in an application. We also need to take into account what expectations a developer will have when implementing this component.
The ultimate goal of asking these questions is to create the most reusable component possible. Sure, each new feature adds time to the equation, but in the long run may end up saving time in the future. It may also prevent multiple components being created which do similar things.
- Will someone attempt to use it as a ReactiveForms control?
- Will someone want to attach a validator of some sort and show the email addresses which don’t match a certain pattern or maybe some other criteria? Should we include validation in the component or should it be assumed whomever is implementing the component will handle validation?
- How do we want to communicate to the user that invalid email addresses are present? A container of invalid addresses or just style the tag/pill differently?
- How many email addresses should we show? What happens when we exceed that number? If we end up hiding email addresses, how do we address question #3 above?
- Will someone want to style it to meet their needs or should the styling be self contained and not allowed to be changed?
- What events might someone need or want to be emitted from the component as things happen?
- Would someone want to add their own UI elements within this component for some reason? Maybe to address #3 above?
- Will someone want to use this to only display valid/invalid email addresses without the ability to add new email addresses by typing into the field?
- Is there anything here I haven’t thought about that would make this MORE reusable?
Answering our concerns
The answers to the questions above will impact how we develop this component. These answers will also affect how reusable the component is and how easy or difficult it is to implement. Our goal is to make it very easy to implement and match a few different use cases. So, I will answer the questions above and include my thoughts around the answer.
- I would think someone may want to use it as a ReactiveForms control. However, making this component a value accessor may hinder some other implementation use case. So, let’s support it but also allow it’s absence. So, we need to add a property so the developer can include this or not
formArrayControl
. - I think this goes to the answer of question #1. ReactiveForms allow the easy inclusion of validators. So I think if we include a ReactiveForms control, validators should be attached to that control. Otherwise, the developer can handle this themselves by writing a function that can be passed to the component. We’ll call this property
validator
. However, we probably still want to include a basic email validator. - This is actually a tough question as there are many answers. We could include a style class on valid and invalid tags/pills. We could put invalid addresses into their own container. We could just display a message, something like: “There are # invalid address(es)”. If this is just for display purposes then that
validator
property may not be present which makes all of these scenarios more difficult or impossible. - Why not make this configurable by the developer? However, we’ll probably want to define a default value. We’ll add another property like
emailDisplayCount
. When that count is exceeded we’ll just put a link to display all of them. - For custom styling, I think it would be good to use css variables with default values. This ensures the component is styled nicely out of the box, but will be customizable if need be.
- The events this thing should emit I think I are pretty straight forward:
addEvt
,deleteEvt
,editEvt
,invalidEvt
. MaybeloadEvt
anddestroyEvt
. - I think this answer also helps address question #3. If we include a slot for custom html, say to display invalid email addresses. However, what if someone wants the same implementation in multiple places? That means they would have to duplicate the html for that slot in each of those implementations. So, I would say we don’t want to support this scenario for that reason. Plus, I feel this makes the component reusability more difficult. However, let’s go ahead and support the invalid address container by adding another property
showInvalidContainer
. That actually adds more to question #3. - I could see someone wanting just the display portion of this component. I think supporting this makes the component more reusable. It will also require another property we’ll call
displayOnly
. - To see if we’ve missed something obvious talk to your team mates and get their input on what they would like to see in an email address manager component.
Development Findings/Adjustments
During the course of developing a component sometimes you get ideas that might be nice-to-haves or something that just got missed in the requirements gathering process. In those cases, time permitting, I usually try to add those features to the component.
While developing this component I did run into other use cases that might be handy. It wasn’t a big deal to add them so they got added. My results probably have bugs but the effort was just to get a result from this discussion. You can find the repository here: https://github.com/keithstric/email-manager. I did this as an angular component, but these thought processes work for any type of framework, library or pure web component.
- I thought it might be nice to have an option to prevent duplicates. So we added another property called
preventDuplicates
and if that is true we prevent duplicates from being added and display a message. We also added another event calleddupeEvt
which is fired even ifpreventDuplicates
isfalse
. - I also added labels for the different containers.
allContainerLabel
,validContainerLabel
andinvalidContainerLabel
. - I ended up not implementing the pasting of multiple email addresses. This got omitted mainly due to time constraints
Conclusions
This article has actually been started, came back to many times over the past couple of months as I was trying to put these thoughts around “any” component. I found that most difficult until I determined the best approach was to just develop a single component and write about the process from that view point. I think that made it easier to think about all the steps necessary.
Hopefully I’ve shown my thought process when developing a new component. I try keep in mind that we want the most robust version of a feature to be available to the developer. Even if the added features aren’t part of the requirements right now, because they may soon be added as a requirement.
The biggest impediment to adding additional features to a component is time. For this we really can’t do anything except get what we can done and maybe leave our thoughts, properties and methods in the component but just not used. This way we get a little bit of a head start when we’re able to come back to the component.
Until next time…. happy coding!