Recently I spoke at Frontend Developer Love about Angular Schematics as a solution to avoid common pitfalls and bad practices when architecting and coding an Angular app. In combination with the Angular-CLI, Schematics becomes a powerful tool to define your project structure, and make sure you can replicate it across many workspaces.
Let’s start by defining the problem. Whether you’re a freelancer, work at a small agency, or are a member of a large organisation, it is likely you’ve come across this scenario: you or your tech lead, or the architect at your project have finally found the right project structure, best practices for dependencies, naming and other conventions you want to be followed by everyone developing Angular projects in your company -or even yourself in the future, if you’re working as a contractor- but, documenting and passing quality gates, becomes an arduous AND expensive job.
In the end, you end up copy-pasting a lot of boilerplate, searching and replacing ID’s in your IDE, manually adding or removing chunks of code or writing the structure folder by folder, file by file. Let’s agree that’s not the most productive way of getting things done.
Yesterday I wanted to have everyone on the same page when it came to understanding the concept of a well organised and meaningful project structure. So I described it with this image.
We have to design our project structure just like the person that designed that window carefully drafted it, to connect each element. They probably very consciously chose the materials, the width and weight, and placed each item strategically, including the steps so you can easily maintain that window clean.
Let’s propose a more concrete problem to solve
Let’s suppose, in this case, that we are pretty sure what’s the folder structure to accommodate our .scss files should look like, but we don’t want to:
- write the structure by hand, adding each folder and file manually
- have to write long relative paths in order to import and resolve our common files as dependencies to other .scss code
- we definitely don’t want to be copying and cleaning from a different project!
Let’s now use schematics to turn all this manual work, into magic
First of all, we need to make sure we have the right node version -latest LTS is fine-, that will allow us run Angular-CLI and Schematics. We also need to globally install the following dependencies in our system, before we start
Once we have our global dependencies installed, we can create our first blank schematics, by running the command below (please notice that I’m passing my-schematics as name, but you can pass whatever your schematics name may be)
$ schematics blank --name=my-schematics
That will create a blank schematics in your system. From where you can start writing your schematics. You can either delete now that schematics, or you can just rename the RuleFactory name, and continue from there.
Wait. The RuleFactory? What’s that?
Each schematic is basically a RuleFactory, that means it is a function that takes options as arguments, inputs a tree (or source, in the case there is nothing in the tree yet), applies whatever Actions you describe and outputs another tree. Each schematics has a schema to validate it, and it’s part of a collection. I know that’s a lot of vocabulary, there. And please notice that I also made “describe” in bold letters. And I will explain why.
Schematics are in the end a descriptive generators that work as a transformation pipeline. And the best part is that, although you can pipe them or chain them together, they are atomic, extensible and reusable.
Generators? What are generators?
Code generators are tools that actually generate other code, templates, etc, on a command prompt. You may have heard of Yeoman, or Maven Archetype, etc.
Let’s go through the important vocabulary you need to acquire, to better understand schematics as generators
- collection: a definition of our schematics. If your schematics are not part of this object, they won’t run.
- schema: the object that validates our schematics input and description
- ./files (tree or source): a virtual file system and its metadata that serves as a staging area
- Rule: a function that takes a tree in and outputs another tree (typically after you applied some Actions)
- RuleFactory: a factory for rules, it takes options as arguments (the ones you pass via the command line)
- Action: an atomic action that transforms your tree in one way or another
This actions are methods of the Tree interface. There are methods to access or visit your file system, to read the content of files in it, to move directories or files from one location to another, but the four more important functions that have the potential to modify your file system, are
I think the names are pretty self-explanatory, so I won’t go into detail. But what I would like to stress is the descriptive nature of schematics. This means everything you’re writing in your factory, is purely descriptive. And schematics, when ran from the schematics project itself, is always a dry-run. Which means that unless you explicitly pass this flag
your file system will remain intact. So you can test your schematics, atomically, and only commit those changes to your tree (stage) when you’re ready!
Watch out, though! If you execute them from your Angular project with
ng generate .my-schematic:my-schematic --options
you’re definitely going to modify your file-system. If you’re using a version control system, you probably don’t have a lot to worry about. But you have to be aware of this!
Anatomy of a schematics RuleFactory
Let’s take a closer look at what one of those rules factories look like, and what they can do. For that, I am embedding this video. As you can see, that’s the schematics generated when creating the blank schematics. I pass the _options.name as name to a file I want to create, I pass an alternative name as string, and the content of the file as string, too. As you can see, the first time I run the schematics, nothing happens. The second time, however, I do add the — dry-run=false flag, and my file system is altered.
What is the schema.json for each schematics and how does it help?
Instead of writing a lot of words here, about the schema and why it’s important, I am pasting a gist with some annotations. Obviously, you save a lot of time in writing exceptions handling, by using the validation schema.
I invite you to fork the demonstration repository from github, https://github.com/anfibiacreativa/frontendddevlove-angular-schematics and start playing with it. You have all the instructions in the readme file, on how to get it up and running, before you keep reading.
The template definitions, or how you actually create the structure ONCE
If you take a look at the above embedded video, you will see I am creating indeed a folder structure manually. But this operation is performed only once. Further more, it is a lot more than just creating a few folders and files. I am actually defining instructions on what the names should look like, and even the content! Everything after two dashes is defined dynamically as described in your schematics. In the case of the video, I am:
indicating the path where the structure should be created at, that is passed as _options.path, and then creating a folder below that will be called after a dasherized string of whatever I pass as _options.name. dasherize() comes with a set of utilities or helper functions inbuilt in schematics. You have a lot more options. But you can also chain your own custom functions and they will be applied to your templates!
I will address here some of the valid questions I’ve got after the talk last evening, they may be relevant for you, too:
1.Why don’t you have the schematics project as a module inside of your angular project?
Separating concerns is always a good idea. When you separate your code this way, it’s more maintainable, specially considering that the schematics will become a dependency to whatever Angular projects you have linked to it.
2.Can I have several Angular projects, or even workspaces, linked to a same schematics project?
Absolutely. And you should. That’s the most important principle of making use of this pattern. The idea of repeating yourself the less, keep dependencies to a minimum, have to maintain the least code, and reuse it whenever possible.
3.In the time it takes to write a schematics scaffold, I could’ve copied a project several times. Why is this a better option?
Absolutely, you could. But would it scale? What would be the maintenance efforts, the documentation efforts, and the ramp up times for new members of your team in the future. Additionally, you only got to perform a quality gate for that particular process once, when you commit your schematics code. You don’t have to engage in large arguments and back and forth over less relevant problems, or style.
4.Doesn’t it get even more problematic as the projects scale. Won’t it create unwanted stuff where you don’t need it?
It may. You need to pay careful to the design of your schematics. Does this pattern suit all cases? Probably not. But what pattern does? There may be a time when a particular schematics does not work anymore for every one of the projects linked. But that’s the coolest part. Since you can pass a schematics to another schematics, to another schematics, over and over, you may want to try writing the smallest, most atomic action in each one of them. That’s a good practice anyway, to preserve single responsibility.
5.What was the tune you walked in the stage with?
Repeat after me… No, that’s the tune. Guess why?
To sum it up
I presented a quite frequent problem, and solved it in a simple way by creating routines via schematics, that will modify your angular configuration files to:
- create an ideal folder structure that you can replicate over and over at your project setup phase, without any manual effort.
- generate an alias to import your class members in an easy way
- easily import .scss files without having to write complex relative paths
Of course, there are a lot more complex stuff you can do with Angular Schematics. In the end, everything you generate through the Angular-CLI is implementing Schematics under the hood.
I truly believe this is a must-use tool for every Angular developer.