April 20th 2020
By Edin Čongo, Front-End Developer at Klika
Ever since Angular CLI was introduced 3 years ago, it followed Angular as a trusty sidekick. And oh boy, it is a strong sidekick indeed - as many developers may know, initial setup of the project can be a pain in the ass (differences between project implementations inside the same solution, bugs that are hard to debug and most important part - it is time consuming in its nature), and CLI offered a great help with that.
CLI automated development workflow by offering tools to initialize, develop, scaffold and maintain Angular applications 1. A lot of fancy words here - but basically, this enabled developers to be more effective by being able to create an entire Angular application 2 by entering a command in the command shell and providing a couple of parameters.
The question then arises - what does this have to do with the topic of this post, how can we benefit from it and how can we boost it even more?
Well, under the hood, Angular CLI uses a library called Schematics 3, which is a template-based code generator used to scaffold Angular elements, with them ready to be used. And for the why part, we must ask ourselves a question - what do we think makes a good project and why projects are usually a mess
In my opinion, there are couple of key points:
This last one can be a bit tricky and challenging, especially if you are working in a big, dispersed team, consisting of people with various expertise levels. Because, in order to keep quality - you need to make sure that agreed architectural guidelines and patterns are being followed and enforce using best practices.
All of this will have impact on multiple fronts:
How to use Schematics to achieve this and boost team output? There are a couple of ways to do that.
Configuring CLI schematics
Sometimes circumstances surrounding your project (requirements, team setup, bad planning phase, unrealistic deadline, etc.) don’t allow you to have extensive preparation, prepare templates and schemas that you would use in your project and you have to dive right in.
Well, there is still a way to utilize existing Angular schemas and enforce your architectural guidelines, best practices and/or technical requirements.
As we already know, angular.json is used to configure our Angular application. So now we can use it to configure and override existing templates for generating, let's say, component and service elements.
Part of angular.json file
Now we just need to define properties to suit our needs.
Part of angular.json file with overridden schematics properties
What we achieved is that now, every time ng generate component command is run, generated component will have a prefix, it will be exported in module and template will be inside .ts file. Same goes for ng generate service, which will be generated without parent directory.
Angular component generated based on overridden schematics properties
Creating custom schematics
Welcome to creating custom schematic 101 - let’s see how it’s done, what are dependencies and possibilities.
First of all, we need to install @angular-devkit/schematics-cli 4 package in order to generate a schematics project. Now we are able to use schematics blank demo-schema command in order to generate schematics project.
Structure of generated schematics project
Let’s analyze the structure.
collection.json file contains definition of schematics inside our schematics project (remember angular.json for Angular application). For reference, let's see the collection.json file inside the @schematics library created in Angular application and used in previously mentioned generation of Angular elements.
Part of collection.json file inside @schematics library, which is installed with @angular-cli in Angular projects
Our collection.json file looks like this:
collection.json file inside our schematics project
As we can see, in our project we have one schematic - demo-schema, which is defined in index.ts file in demo-schema folder (also has .spec test file).
Index.ts file inside our newly created schematics project
To break down this file:
We have a factory function demoSchema, which is parameterized with _options argument and returns a Rule. It is in the form of a factory in order to be adjustable (remember ng generate component with various flags as options).
Rule is called with two arguments, tree and _context, altering the tree and returning it, so it can be altered again if need be (we can have more than one rule).
Finally, tree can be considered as a virtual collection of files inside a workspace - so when we are running schematic, changes to tree are run in a transactional manner (commit comes after successful run) which ensures fast and desired result.
And now we are finally ready to create our own schematic.
First of all - let's create our own schema, which will represent a blueprint for our demo schematic.
We defined that we have one property, name, which is string, is required and we have a prompt for it. Now we just have to update our collection.json file, to reference which schema our schematic will use.
Updated collection.json file with schema path
Next thing is a template, a file which our schematic will use as a template when creating. So, let’s create a folder named files inside our schematic project (we are using ‘files’ because it will be excluded from Typescript compilation by default as defined in tsconfig.json, and it has to be excluded because templates should not be compiled).
Inside the newly created folder, we will create a demo.ts file (our template).
Content of our template
<%= and %> represent tags in the Schematics template when we want to print variable value.
And finally - we need to update our index.ts file.
Updated index.ts file
We are fetching template from files folder, transforming template by applying provided options argument on it and finally merging our transformed template into the tree.
Now, in order to run our schematic, we need to build the project by running npm run build command, and then running command schematics ./demo-schema/src/collection.json:demo-schema --debug=false in order to run our schematic.
--debug=false is used because we referenced our project with relative path, in which case schematic is executed in debug mode, which is basically equal to running schematic with --dry-run flag (no changes are committed in the end).
Running of schematic
Content of created file
This was just the tip of the iceberg, represented by a simple example of using Schematics to create a simple .ts file, which should be a bait to everyone interested to dig deeper and explore every possibility it offers.
This can be a powerful tool in anyone's belt, eventually making our life easier and helping us to focus on more important parts of a project.
1. More information available on https://angular.io/cli
2. Also includes ability to build and serve application with live reload feature, generate components, services, guards and other Angular elements, run tests and more (more information available on https://angular.io/cli)
3. More information available on https://angular.io/guide/schematics
4. More information available on https://github.com/angular/angular-cli