My Favorite Tips and Tricks in Angular
Angular comes with so many features, both popular and unknown, the easiest way to discover tricks to achieve difficult tasks using Angular is to use Angular a lot more and learn in the process. Here are my favorite Angular tips and tricks.
Angular is a JavaScript framework for building web applications, especially single page applications. As a framework, it offers the best practices and tooling to easily develop these web applications. When building with Angular, you’ll be using declarative templates, dependency injection, etc. to power applications that can run on all platforms (web, mobile and desktop).
Angular already outlines its best practices for easy development using the framework, but there might be other tips that you’ve missed that will most likely make development easier or help your application run and load faster. So here are seven tips and tricks to making Angular applications better.
1. Use Services to Handle Side Effects
When building your application, it is always useful to reduce side effects like HTTP requests, time-based events, etc. Abstracting these from the component to services will help reduce the complexity of the component and also ensures the reusability of the service. An example would be fetching data from an external server. You could fetch data within your component like this:
TypeScript
This method used in fetching the items is local to the component and can’t be reused, and if items are being fetched in other components, this whole procedure will be repeated and that’s not very DRY. If multiple network requests are made, the component will be littered with these methods. Let’s refactor this component to use a service for external requests.
TypeScript
Then we’ll make use of it in the component:
TypeScript
This service can be used to fetch items application-wide.
2. ng add
This utility, introduced in Angular version 6, can be used to add a published package to your work environment, and it’ll run schematics in the background to update the functionality of your application. When downloading a package using this command, it also installs extra dependencies it needs to run, like polyfills, etc. Your application can be converted to a progressive web application using service workers and providing offline functionality using the command.
You can implement progressive web application features in your application by running the following command:
Bash
Or if you wish to add a touch of Material Design in your application, you can add the Angular Material library
Bash
3. Web Components
From Angular version 6 onward, you can develop custom native elements that can be used outside Angular. This can be done using a package introduced by Angular called Angular Elements (@angular/elements
). This package provides a way to createCustomElements
and polyfills to support browsers that aren’t compatible with web components. With this package, you can package your favourite components and use them within other frameworks like React, Vue, etc.
To get started building custom native elements in Angular, install the Angular Elements package in your application using the following command:
You can follow the quick tutorial in the official Angular documentation to get started.
4. Aliases for Import Statements
This very useful feature is supported out of the box in Angular. I’m sure you’ve encountered instances where imports in your applications are just messy and difficult to read. You have something like:
I’m sure it would be more helpful to have aliases for the components
and services
paths – this would make these imports relatively easy to read and import.
When working with React, I’ve researched how to achieve this, but most solutions involve ejecting your application, which doesn’t really sound pleasing. Well, to achieve this in your Angular application, all you need to do is to update the tsconfig.json
file:
What happened here is that the default value of the baseUrl
property ./
was updated to point to the src
directory. Then we added a new property called paths
, which is an object containing key values pairs representing aliases defined for paths in our application. Aliases were defined for the components
folder and the services
folder. Now if we want to attempt the imports in the previous example, we’ll do this:
This is way cleaner and easier to read than the previous example. If you’ve not booted up your editor to do this for your application already, then you should get to it.
5. Safe Navigation Operator for String Interpolation
When working with objects in Angular templates, you encounter situations where variables are declared without default values – the variable is just presented as a type definition. When trying to access a property on the variable that isn’t readily available, Angular will throw an error saying the variable is undefined.
For example, your template looks like this, you’re reading the name
property of a student
object:
And this was how the variable was declared in the component file:
Angular will throw an error here.
Using the safe navigation operator, we can safeguard the name
property against any null
and undefined
values. The safe navigation operator in Angular is this syntax ?.
, and we can update the template to use this:
When you run this, Angular doesn’t throw any errors and your console is clear. Another useful technique of avoiding this error is using the and (&&
) operator to check if the value exists before reading the property path. We can update the example to use this syntax:
If the value doesn’t exist, Angular will avoid evaluating the expression and nothing is rendered between the tags.
6. Handle Errors Properly with an Error Handler
Angular comes packed with an exception handling service that can be used to manage errors application-wide. When the service detects errors, it catches the error and logs it to the console. This service can be extended to add additional features unique to our application like logging the error using an error monitoring platform or sending the errors to your server for analytics.
The Error Handler is pretty easy to extend: We need to create a class
that extends the properties of the ErrorHandler
and overrides the built in handleError
method used for displaying errors.
Create a file called error-handler.class.ts
:
In the snippet above, we made use of a fictional error reporting and monitoring library called @error-reporters
. After extending the ErrorHandler
service, we will report errors emanating from the application in the handleError
method before handling the error with the ErrorHandler’s handleError
method.
After that, we should register our custom AppErrorHandler
in app.module.ts:
You can read more on the default error handler by Angular here.
7. Lazy Load Non-Vital Components
When working on fairly large applications or starting up one, it will be helpful to ensure that components not needed for the initial render of your application are lazy loaded. Lazy loaded in the sense that they’re loaded on demand. For example, when a user navigates away from the initial view of the application, a network request is made to load the destination route. Lazy loading can effectively reduce the bundle size of your application, thus reducing the load time of the application on the browser.
Lazy loading components starts with creating a feature module in your application, the feature module will house the components, services, providers, etc. attached it. The feature module is then loaded in the root routing module of the application. Look at the example below:
This feature module FeatureModule
contains a single component FeatureComponent
and a routing module FeatureRoutingModule
attached to it.
To lazy load this component, we’ll register the feature module’s routing module in the application’s root module:
With this simple step, a separate bundle will be built apart from the main app bundle. This bundle will be loaded when the user navigates to the /feature
route. The experience might be a bit unpleasant because the user will need to wait for the route’s bundle to be loaded, and this might take a while depending on the size of the bundle.
To fix this issue, we’ll prefetch the other bundles in the background once the initial page has been loaded fully. We can do this using a built-in flag provided by Angular called the preloadStrategy
. This tells Angular which strategy to use when loading lazied bundles.
Let’s update the current implementation to use the PreloadAllModules
strategy:
With this update, Angular will handle prefetching of feature bundles in the background for easy navigation.
8. Define form parameters before Component definition
9. Use renderer addClass, removeClass where possible. It will prevent too many change detection checks.
10. Try to use get, set on Input() instead ngOnChanges. When you have many Inputs in the ngOnChanges, each “if” has to be checked.
11. Use pipe when rendering content in ngFor
12. Add baseUrl and path to your compilerOptions to avoid any inconsistency when importing other files
13. Add stylePreprocessorOptions to your angular.json file to avoid inconsistency while importing other files
14. Run npm outdated
command or add npm-check once a month to keep your dependencies updated. It will definitely help you keep track of changes. It’s much easier to update Angular 5 to 6 than 4 to 6.
npm outdated
command or add npm-check once a month to keep your dependencies updated. It will definitely help you keep track of changes. It’s much easier to update Angular 5 to 6 than 4 to 6.15. Run npm audit command once a month to check if any of the libraries has any vulnerabilities. It will help you keep your app secure.
16. Use parent in form validation instead of this.form which may not be initialised while doing/performing custom validation check.
17. Keep route names as const. It will prevent accidental typos.
18. Start using webpack-bundle-analyzer. It will help you detect any fast-growing modules.
In our case by mistake main.scss has been included in another file instead variable.scss. It has doubled the size of the bundle !
19. Use browser Performance tests. 17ms rendering time means you use 60fps. Actions with fewer than 60fps and more than 30fps are ok. Any speed below 30fps makes the user notice the visualized slowdown of the application.
20. Use Augury chrome extension to track the current state of components.
21. Prettier as code formatter
Example config:
To prevent conflicts between tslint and prettier use
22. declare keyword – create a custom type when the native one doesn’t exist. It’s great for typing in if the JS libraries hasn’t been typed.
23. Declare dictionary key and value types arg: { [key: string]: number }. Each value of this object will be typed as a number
24. Ampersand operator:
25. Declare tuple types
26. Big figures You can use _ as a digit separator to make big figures more readable
let bigNumber = 123_456_678;
let bigNumber = 123456678;
27. Abstract class
28. Reduce the number of any types:
Add no-unsafe-any: {“severity”: “warning”} to tslint – you will get a warning when any type is used in the code.
To track the number of unsafe-any exists in your project by `npm run lint | grep WARNING | wc -l`.
To prohibit an increased number of unsafe-any add this bash script to your CI pipeline.
this will cause the pipeline to crash when the number of unsafe-any will be greater than 100.
29. Add no-string-literal to tsconfig – disallowed to access key by obj[‘key’], only obj.key allowed.
obj[‘key’] won’t throw a compilation error, even if the key is not defined. It’s a good way to prevent “cheating” on TypeScript.
30. Force generic types:
“no-inferrable-types”: [
true,
“ignore-params”
],
Will crash lint when explicit type declarations are found e.g.
31. Reduce code complexity
Add “parameters-max-number”: [true, 10] to tsconfig – it will set the maximum number of parameters that can be added to the function.
“cognitive-complexity”: [true, 10] – disallows you to add more than 10 “if/else/switch” statements in the function.
“no-big-function”: [true, 100] – set the maximum number of lines per function
32. Keep your codebase clean and dry:
no-commented-out-code to tsconfig – doesn’t allowed to keep commented code
“no-duplicate-string”: [true, 5] – linter will crash when it finds the same string used more than 5 times. It forces the programmer to create variables for common stuff/ elements.
33. Add “noImpicitAny”: true to tsconfig – it will throw a compilation error when the types cannot be inferred or inferring them might result in unexpected errors
34. Add “noImplicitReturns”: true to tsconfig – it will throw a compilation error when you try to return different types in each if statement
35. Add “strictFunctionTypes”: true to tsconfig – it will throw a compilation error when an incorrect parameter is applied to the function.
36. Add “noUnusedParameters”: true to tsconfig – it will throw a compilation error when an unused function parameter is detected. Arguments started with underscore are allowed.
37. Add “noUnusedLocals”: true to tsconfig – it will throw a compilation error when unused variables or imports are found in the code.
Last updated