I didn’t realize it, but some recent posts to this blog have had a bit of a theme. As I work on a couple different projects and we really get into the final stages, I’ve started to realize that sharing is very important.
When you have a medium-large sized web application, there will be a good deal of code written. It becomes more and more vital to reuse or share as much of that code as possible between different areas or components. Any intro to development guide will give very good reasons why: unifying the look/function of your app, making it easier to fix bugs, as well as easier to add functionality. If a feature uses a base class or service for a good chunk of it’s behavior, and that behavior is shared by other features, editing one base class or service is much easier than trying to find/replace across multiple files.
It’s even more important with a team. Generally, medium-large sized web applications are not written by one developer. When more programmers get involved, having shared services becomes even more vital to avoid confusion and duplication of work.
So we have some (15-20) views on our Angular 2 application that will all show some kind of single item grid. These will be two columns in a database (key and name) and when displayed on the app, will just show the name and a column for ‘action’ (edit/delete). There will be a search text box at the top, a row at the bottom for adding a new entry, and will include the pagination component.
We already covered creating that pagination component in past posts, so all we had to do for that is drop the component’s selector into the template and import the related service. We did this on a shared ‘single-column-grid’ class- all our actual components that will use this view will extend from that class.
But in this case, we found we could go one step further. Each screen will be so similar, and only the data will change, that we created a shared ‘single-column-grid’ html template. By using generic variable names in the template and component class, each component can ‘hook into’ the template and use it as needed.
This dropped the number of html templates needed from about 20 to 1 for this aspect- reducing a whole lot of copy/paste and the typos/wasted debugging time that come along with it. Also, if the design team wanted to make a change to the layout, they just needed to edit one file, not 20.
One of my favorite tricks from the couple shared base classes we’ve created for this project relates to Angular’s lifecycle hooks. These are very cool callback functions that fire at certain points in a component’s life- the most recognizable are ngOnInit and ngOnDestroy. OnInit is very useful for loading data from a database into the component and calling any other initialization type methods you need for this section of your app to function. However, these only exist on a full ‘@Component’ in Angular- they are not available on a simple ‘@Injectable’ class. The problem is, we need to do certain tasks when our component initializes, and some of those involve updating the base Injectable class.
At first, I thought we could put the necessary logic in the base class constructor, but that only gets called on class construction (duh). Because we had multiple child classes sharing that parent, those children would all have to be able to update the parent. The answer turned out to be: create our own, super simple, onInit! In the base class, we created a method: serviceInit. In this method, we have some calls to other methods on the base class that need to fire each time a new component that inherits from it starts up (setup text search, pagination, get initial data from db). Then, in the child class, in the onInit method, we simply call this.serviceInit() after initializing any class variables. For example: there is the backend api url- different for each component that needs to get data from the database. So, in the child class, we just set that variable (and a few others), then call the init method:
this.apiUrl = CONFIG.componentApiUrl; this.serviceInit();
And the base class serviceInit method takes care of the rest. We end up having to do very little on the child component- setting up any form that might be specific to that screen only, unsubscribing from any Observables in onDestroy, etc- but most of the logic is in the parent. If any member of the team needs to create a new component, it becomes very simple to do so. And if they need to add or fix some functionality of the shared service, they just edit that one file and the fix propagates to any component that inherits from the base.