Create an InvoiceService which will handle complex operations, transformations, etc.
You could do this with presentational components too, and if you need a service instance per component, just declare it with a provider in the component itself, instead of providing it at module-level.
Your containers shouldn’t make assumptions.
Let’s say your component has a button and in order to know what to do when the button is clicked, you’ll have to consider other variables (the url, maybe): don’t let the container decide that!.The container’s only job is to say that the button was clicked (by calling a method on a service or dispatching a Redux action like “ButtonClick”).
It’s the service, or a Redux Effect, which will decide what to do next, based on the rest of the app’s state (ex.
Am I under the route /invoices? The button click may create a new invoice.
Am I under /estimates? The same button click may create a new estimate).
Containers may be split.
If your route’s view is simply too large, and has completely different sections, create more than one container, each one with its own data (from services or store selectors) and methods (service methods or dispatched actions).
Components with duplicated logicIf two components look quite the same and behave quite the same, you may not need two components.
This is such a big topic and it may deserve an article on its own, but here are some guidelines:Use Directives.
In the Angular world, we have Directives, which are Components without a template.
Instead, they can inject the template of the component they’re applied to!.In other words, look at directives as a way to incapsulate behaviors and reuse them.
Do you need your CardComponent to open a modal when clicked?.Don’t make a CardWithModalComponent, please.
Make a directive which listens for clicks on the host element and apply it to specific components.
Does your Directive need data?.Pass it to the directive or declare some other Inputs in the directive itself and use them on the same element.
Example:Delegating behavior to a directive.
modalTrigger is an additional Input of WithModalDirectiveUse Content Projection.
It’s used way too rarely, yet it’s an amazing practice and a useful pattern that you can use with every component-based framework or library.
Take the CardComponent example from above, and suppose you want to create an element to display multiple cards:Consider reusing components with Content ProjectionThis way we don’t have to duplicate our CardComponent’s structure and we’re free to manage all the cards from outside (inputs, outputs, applying directives, etc…), the wrapper could only provide some styling (putting them in a row? Masonry? Carousel?).
Also, you can use ContentChildren to get a QueryList of all the CardComponents, and react to changes.
Obviously, you may still go for the “Don’t” example if you really need to have a different component.
Huge FormsIf your Angular forms become huge, there’s really only one thing you should consider:Use ControlValueAccessor to create sub-forms.
This is what you need to learn if you’re building complex forms.
You’ll find lots of tutorials online, and even though it may seem scary, it really isn’t: it’s just an interface that we can implement in our components to make them act as valid FormControls.
It helps keeping the form state united, it’s the best practice, it works with Template-driven and Reactive forms at the same time, and it’s really easy to use, trust me.
Once you’ve used it once, you can almost copy-paste the implementation for other components.
For example, let’s say you have an Invoice Form, this could become the component’s template when you create custom controls:Invoice Form (pseudo-code)Other advantages of this technique are:Your sub-forms become reusable in other components!They’re components: you can still use Inputs and Outputs to customize them (maybe app-invoice-item needs the list of all your products/services to choose from? Pass it as input! Are you creating a new product directly from the app-invoice-item because the user has typed something which is not in the products list? Emit an event, and the container will create the new product with a service/action!)You can validate them from the outside just like a regular input (recommended) or from the inside (if the logic never changes) implementing the Validator interfaceSince the whole form’s state is in one single place (instead of being split between sub-components) it’s really easy to serialize/deserialize and to persist in a service, store, browser storage, cookie, database, etc…It’s satisfying ;)Not studying RxJS enoughAngular is built upon 2 things: TypeScript and RxJS.
Ignoring them isn’t a wise option if you want to use this framework comfortably.
But if TypeScript is quite simple for someone who used to develop with a statically typed language (C, C++, C#, Java…), RxJS could be a tough topic even for the experienced developer.
Reactive Programming requires a shift in your brain, but once you grasped it, it really, really pays back.
Don’t just look at the surface, go deep and you’ll find that the Angular Team didn’t choose to include it just for fun.
In my humble opinion, it’s maybe the greatest choice ever made by the team.
You may find that the team has given you a Ferrari, and maybe you’ve always used it like it was a subcompact.
Here’s what you need to learn to be able to overcome even the toughest tasks:Learn how to use flattening operators (mergeMap, switchMap, concatMap, exhaustMap) and how they suit different scenariosLearn how to combine multiple streams (combineLatest, withLatestFrom, forkJoin, merge, race…)Learn how to manage subscriptions and how to filter streams (unsubscribe, filter, take, takeWhile, takeUntil…)Look at all the operators: you’ll find that you don’t need to remember them all, as you’ll be able to guess the ones you already saw even months before.
You’ll also be able to guess some of them’s behavior just by looking at their name (ex.
if you know what takeUntil does, would you guess what skipUntil does?)Learn the difference between hot and cold observables and finally realize why this naming convention doesn’t really cover all the scenarios (might deserve another article, what do you think?)Learn how to use Subject, ReplaySubject, BehaviorSubject and AsyncSubject (tip: although BehaviorSubjects are absolutely not and will never in the universe be an alternative to Redux, you might consider them as a sort of “state” with an initial value ;))Also don’t forget that RxJS is a library on its own, and you could use it tomorrow with any other framework or library (or even language!).
The reward is guaranteed.
ConclusionI hope this article was useful, if something else pops into my mind I’ll update it as soon as possible 🙂 If you have any question… well, I’m sorry if it took me ages to respond in other articles, I’ll try my best this time!.Cheers!Post Scriptum: are you from Italy?I recently decided to open a new YouTube channel which will contain some VLOGs and will soon contain tutorials, tips and tricks, lessons and other funny stuff.
Here it is.
I hope to find the time not only to make videos, but to make english subtitles available!.