React
With Props Spreading, Dependency Injection via Context API, and TypeScript via TSX, React does an excellent job at providing a powerful composition model for building UIs.
On the other hand, Angular with Directive, built-in Dependency Injection, and recently Host Directive also makes a great choice for building UIs with composition in mind.
Regardless of your preferences, this article is meant to show the differences and similarities between different APIs of React and Angular. Knowing these will allow us to easily interop between the two technologies; making our knowledge richer, and the web ecosystem a better place overall.
Selection APIs
In this article, we will be looking at two R3F components that are used together: Selection
and Select
.
What do Selection
and Select
do?
In @react-three/postprocessing, there are a few Postprocessing effects that are applied to only selected 3D objects. Selection
and Select
are used to track the selected objects so the effects can apply to them. Here’s a quick example with Outline
This is straightforward and follows the composition model of React pretty well. We want to enable selection, wrap the scene with Selection
. We want to enable the objects to be selectable, wrap them with Select
.
Though at this point, I want to point out a couple of things about Select
specifically. Select
is a component that renders a THREE.Group
(via <group>
) so it can be used to select multiple objects at once.
However, if we want to select a single object, we still have to wrap it with Select
like the original example. This feels a little awkward to me, but it’s the composition model of React.
One good thing about Select
and React composition model is that it allows for Select
to accept any properties that are valid for a THREE.Group
via Props Spreading. This makes the Select
API itself very flexible and a lot less code to write. Main example of this is the onPointerOver
and onPointerOut
handlers.
Well, that is enough about React for now. Let’s move on to Angular. We’ll port both Selection
and Select
to Angular for similar capabilities. During the process, we’ll dissect the React code to see how it works and what’s the best way to port it to Angular.
Porting Selection
to Angular
Let’s bring back Selection
and dissect each part of the component.
With this in mind, we can kind of guess how Selection
API is consumed. Children of Selection
will access the API via useContext(selectionContext)
and that is exactly how Outline
consumes the Selection API.
We’ll start by stubbing out NgtSelection
in Angular
1. The template
Usually, the first thing that I port is the template.
Angular
In this case, Selection
renders its children because it needs to provide the selectionContext
. For Angular, we can use ng-content
. However, that is actually redundant because we already have the NgtSelection
class itself as the context. So, we can change NgtSelection
to a Directive instead.
React
Angular
2. The value
Next, we need to have a way to keep track of the selected objects.
React
Angular
3. The enabled
input
Finally, we need to expose the enabled
input from NgtSelection
to replicate the enabled
prop in Selection
React
Angular
And that is it for NgtSelection
. We don’t need anything from Angular to replicate the Context API because Angular has Dependency Injection built-in. The consumers can simply inject(NgtSelection)
and use the public APIs. Here’s the complete code for both Selection
and NgtSelection
React
Angular
4. Usage
Let’s compare how we use NgtSelection
in Angular to how we use Selection
in React
React
Angular
Pretty cool right? Now, this is even cooler. In Angular, we can also use NgtSelection
as a Host Directive.
Porting Select
to Angular
Let’s look at Select
next and dissect its code.
As mentioned above, Select
has both pros and cons. It is flexible and can accept any properties that are valid for a THREE.Group
via Props Spreading. However, it always renders a THREE.Group
even when we only want to select a single object.
Similarly, let’s stub out NgtSelect
in Angular
We’ll follow the same approach as before; starting out with the template
1. The template
React
Angular
Well, this is straightforward. But this is also where things get a little bit interesting. We need to make sure that NgtSelect
can pass-through any properties that are valid for <ngt-group>
from outside. In Angular, this is not possible with the Input model. Not to mention, we also need to pass-through the event handlers that are valid for <ngt-group>
as well; Inputs and Outputs are separate concerns in Angular, unlike props in React.
So, how do we proceed? We have 2 options:
- a. Replicate all the inputs and outputs that
ngt-group
potentially accepts - b. Turn
NgtSelect
into an Attribute Directive instead of a Component
Option (a) is straightforward to understand but it is also a lot of code to write (and maintain). Option (b) seems complicated and sub-optimal at first. However, it allows us to delegate the responsibility of attaching NgtSelect
on the consumers’ selected elements (i.e: the <ngt-group>
), and in turns allows NgtSelect
to NOT have to worry about passing through anything. So, we’ll go with Option (b).
Now compared to Select
in React, Angular version will make the consumers do a little bit more work by using NgtSelect
with ngt-group
instead of making that decision for them by rendering <ngt-group>
This is actually a good thing which we will discuss in a bit.
There is one more thing that we can do for NgtSelect
. Since NgtSelect
is now an Attribute Directive, we can control when it is instantiated by using the selector
property. In this case, we only want to instantiate NgtSelect
when the host element is <ngt-group>
so we’ll adjust the selector
to be ngt-group[ngtSelect]
Additionally, we can also make a decision that NgtSelect
can be used on <ngt-mesh>
as well, allowing the consumers to select single objects. Once again, we’ll adjust the selector
to be ngt-group[ngtSelect], ngt-mesh[ngtSelect]
2. The enabled
input
This is similar to NgtSelection
, we’ll use Signal Input here to replicate the enabled
prop in Select
React
Angular
3. The group
reference
NgtSelect
is an Attribute Directive, meaning that we can inject
the host element with inject(ElementRef)
. This is almost equivalent to useRef
in Select
in terms of functionality and purpose.
To be perfectly clear, I’m not saying that
inject(ElementRef)
is the same asuseRef
in React.
React
Angular
4. Consuming Selection API
NgtSelect
is going to be supposed to be used within NgtSelection
so natually, we’ll have access to NgtSelection
via inject(NgtSelection)
in NgtSelect
React
Angular
5. The effect
I mentioned earlier that the effect body is irrelevant here. So we’ll just point out which API from Angular that we can utilize to execute the same side-effects as Select
in React.
React
Angular
And that is it for NgtSelect
. Let’s see both versions side-by-side
React
Angular
Now that we have NgtSelection
and NgtSelect
ported, let’s compare their usages between React and Angular
React
Angular
Let’s see NgtSelection
and NgtSelect
in action
Conclusion
In this article, we’ve explored the differences between React and Angular in terms of how they handle composition. Angular, while being a more opinionated, still provides a lot of flexibility for composition. The difficult parts are to know what’s available and the trade-offs of each approach. I hope that you learned something new and have fun learning it. See y’all in the next blog post.