Reusable Presentational Components in Angular 6+

Have you ever felt the pain of writing & maintaining the same code pieces at multiple places?It may be a small code snippet or a third party component, the pain & frustration is constant.

Ridhi Gajera
Riddhi Gajera

November 17, 2021 | 5 minutes read

Problem Statement

When it comes to the services or data storage in UI applications we always think and write reusable code. But when it comes to certain views like tables and charts, which are not only extensively used across the application but also highly dependent on the theme and third-party library, We never give a thought about making it reusable, instead end up writing redundant code which drags development speed and productivity. So the questions are
  • How to create a reusable view?
  • How to make enhancements and change adoption faster with more consistency?

Understanding the Problem in deep

So the answer to the above questions is to create the common component and use it everywhere seems easy, right?

Wait… let’s take Tables as an example and think about the tables’ view with the below cases where

  1. Each table shows data fetched from various API
  2. Each table has a different number of columns where each column can have different types of data like strings, numbers, currency, links, or HTML elements.
  3. For Each table having different styles for various data types as follows
    E.g. :
    1. If data is a string then align it to the center of the cell.
    2. If data is currency then align it to the right of the cell and show it with the “$” sign.
    3. If data is simple number align it to the left.
    4. If data is links want to show in a specific style and different behaviour
  4. Want to combine the value of two fields and show it in one cell.
  5. Want to provide the row selection on a few of the tables.
  6. Want to show a very specific message when there is no data available.
  7. Want to add data sorting capability for a specific columns or for all.
  8. Want to have the pre-selected rows on the table while loading the page.
  9. Want to hide the headers for some tables.
  10. Want to provide the global filter for specific or all columns of tables.
  11. Want to provide input box on certain columns and provide value changes
  12. Want to provide input box on certain columns with event which trigger on value change.

Assume if you need to support this on each page with tables that represent the different data how much redundant code one needs to write, so what’s the solution?

Dividing Problem into solvable chunks

Below are the building blocks to reach up to the solution and remove our pain 😉
  1. Presentation component.
  2. Table level Configs
  3. Column specification and Column level configs.
  4. Data
  5. Events to emit the actions on table or columns.

Presentation component

Here comes the actual savior 😉 The presentation component will work as a dumb component and it will
  • Use the @Input to get the all required configs & data.
  • Use the @Ouput to emit all the events happening on the table.
  • Render the view of the table based on specific theme or UI frameworks like PrimeNg, Angular Material, or Bootstrap used within the application with the help of the configs and data

Table level Configs

So if any component has a very specific requirement then it can be passed on with the help of the table level configuration. This config is optional so in case there is no specific config then the presentation component will render table with the default configuration. Will create the type ITableConfig.ts which specifies the required and optional configs

selection: Specify weather to provide the row selection to table or not.
selectAll: Specify weather to provide default selection of all rows of table on load or not.
defaultSelectedData: Provide data that needs to be selected instead of all rows.
disableSelection: To disable the row level selection on the table.
showCheckbox: To Specify weather to show the checkbox for all rows or not.
showShort: Specify weather to provide sorting for each column or not.
showHeader: To show or hide the header on the table as its optional config default value will be true.
globalFilterFields: To specify on which field table level filter needs to provide as its optional config default value will be an empty array.
emptyMessage: To specify the message which needs to show when there is no data, as its optional config default value can be a generic message like “No Data Available”.
scrollHeight: To specify the height of the table after which needs to show the scroll in order to make sure table takes only specified space while there are huge data, its optional config when there is no value provided it will take the whole page in case of a huge amount of data rather than showing scroll on the table

Column specification and configs

Will take the all columns specification with column level config

Will create the type ITableColumns.ts which specifies the required and optional configs at table level

  • field: Use to specify the property name to take from the data, its required config.
  • header: Use to specify the header to show on the table, its required config.
  • order: Use to specify the order of the column on the table, in order to provide the drag feature for columns.
  • class: Use to specify the style class in order to provide specific style at column level.
  • width: Use to specify the width of a specific column render.
  • pipe: Use to specify the pipe name to transform the values on columns.
  • render: Use to render the specific value with current field value or to combine the value of 2 different field.
  • showShort: Use to specify the sort option to the specific column when no need to provide the short option to all the columns.
  • isHtml: Use to specify whether to directly display the value as is on columns or need to render the HTML element and then display the value.
  • showComponent: Use when the isHtml config is true and need to show specific HTML elements like links, input field, or checkbox in the column
  • componentName: Use to specify the component name when showComponent config is true, component name will be predefined and provide on the table component.
  • componentData: Use to specify the properties of the specific component will going to use if its links then will specify the name, class, and action name which will use by the invoker component to identify which link was clicked on the table’s row componentData can have the various type of HTML element and each HTML element have the different attributes which will be specified with the element level required attribute and will use the specific type as mentioned like ITableLinks, ITableCheckboxData, ITableInputBoxData, ITableRowActionData

ITableLinks

  • name: Specify the name for links.
  • label: Specify the display name for the links.
  • class: Specify the style class for links.
ITableCheckboxData:
  • name: Specify the name property of the checkbox.
  • disable: Use to specify the weather need to disable the checkbox based on some data or condition.
  • checked: Use to check/ uncheck the checkbox based on the conditions.
  • value: Use to set or get the value of the checkbox.
  • propertyName: Use for data binding on the checkbox.
ITableInputBoxData:
  • name: Specifies the name of the input element.
  • type: Specifies the type of the input field like text, number, password.
  • value: Specifies value for input field.
  • text: Specifies the text in case want to display some text after the input field
    E.g. :
    Added the number input element to get the percentage value from the user so after input field, you can show the sign of the “%” using a text field.
  • class: Specifies the style classes for the input field.
  • min: Specifies the minimum value, if the input field type is number.
  • max: Specifies the maximum value, if the input field type is number.
  • step: Specifies the legal number intervals for an input field.
  • disableKeyDown: This flag is used to disable the key down event on the input field based on the data.
  • ITableRowActionData: This is used to specify the action name in order to emit the event from the table.

Benefits of creating such presentational component

  1. Presentational component works as the well defined interface which makes it easy to accommodate any changes from theme or different UI library with minimum effort and time.
    E.g. : 
    1. Let’s say here the table component is using the PrimeNG library for table view so in the future on PrimeNg upgrade if there are any breaking changes or property name changes then only a single point of change is there.
    2. Want to replace the PrimeNg completely with Angular Material then also there is a single point of changes and will update on the entire application
  2. Create room to focus on a new development rather than wasting time & effort on the same things repeatedly.
  3. Improve speed of the development and can enhance the presentation component as new requirements and needs come into the picture.
  4. Reduce the testing efforts
  5. Increase the code readability