This is the next post from the series about comparison between Angular and React. In the first two posts (part 1 and part 2) we saw the differences between Angular and React components in terms of creation, rendering, and configuration. In this post I will discuss the differences in terms of templates. We have already seen some examples for Angular and React components, but in this post I aim to give you some more details about what we can do with these.
Assigning a template to a component
The idea of a component is to encapsulate logic that is only related to the component itself (and hence not exposing it to the surrounding world). Components usually have a template, which they work on – attach event listeners, read or write properties, etc.
React
React components have tight relation with the template – each component must render HTML (or any other React component). This is built in React’s mindset as components are the ones to define their templates. Note: talking technically, a React component is actually allowed to return
1 |
null |
or
1 |
false |
, which means that the component does not want to return anything. However, these cases have a very limited application. Moreover, React utilizes a virtual DOM, so it needs to know which components it is going to deal with and calculate the differences with the real DOM before the actual rendering.
1 2 3 4 5 |
var Hello = React.createClass({ render: function() { return <h1>Hello</h1>; } }); |
If you try to return anything else from the
1 |
render |
function, you will get the following error:
Note that the
1 |
render |
function returns a wrapper for a React component or a HTML element, rather than pure HTML. This is necessary due to the virtual DOM mechanism React employs.
Angular
Angular is more flexible as directives are always attached to an existing HTML element. Furthermore, Angular works directly with the real DOM. Thus, we have three ways to assign a template to a directive: inline (similar to React), with an external URL, and no template at all. With Angular we have access to the HTML element a directive has been attached to. So even if we do not specify any template, we can still work with a HTML element (similar to the Decorator pattern in a way and hence the new name in Angular 2).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
angular.module('app', []).directive('hello', function() { return { // inline template: '<h1>Hello by Angular</h1>', // or external link templateUrl: '/hello.html', link: function (scope, element) { // here we have access to element // if we do not specify either template // or templateUrl, element's inner HTML // won't be updated } }; }); |
Generic content
Sometimes one would want to create a component and put whatever content (including other components) inside it (nested at different levels). This enables us to create generic components that can easily be reused. This is a typical scenario the Composite pattern solves. Let’s examine how both Angular and React can help us implement this scenario.
React
React provides a reserved property that gives access to all the children of the component –
1 |
this.props.children |
. These are the children provided by the parent of our component, not the component itself.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var Hello = React.createClass({ render: function() { return <div> <h1>Hello!</h1> {this.props.children} </div>; } }); var component = <Hello name="World"> <em>the</em> <strong>re</strong> </Hello>; React.render(component, document.getElementById('container')); |
This will render a simple (yet nice!) heading followed by whatever the inner HTML of the component is. It is a bit tricky the way
1 |
this.props.children |
behaves, though – if there is only one child, then the property return directly this child as a component, otherwise it returns an array. If you just want to render the children (as shown above), you don’t have to care about it. But if you want to do some work with thm, you have to keep this in mind. To help developers reduce checks of the type
1 |
Array.isArray(this.props.children) |
, React provides some helper methods to deal with children.
Angular
If you only want to work with the children of a HTML element, you can define a decorator directive (as described above) and access them directly from the
1 |
link |
function. You can play with these children as you wish, as you are accessing them directly. To achieve the same idea as shown in the React demo above, however, Angular provides a mechanism, called transclusion.
1 2 3 4 5 6 7 8 9 10 |
angular.module('app', []).directive('hello', function() { return { transclude: true, template: '<div>' + '<h1>Hello!</h1>' + '<ng-transclude></ng-transclude>' + '</div>' }; }); |
1 2 3 4 |
<hello> <em>the</em> <strong>re</strong> </hello> |
The transclusion itself compiles the inner HTML of the decorated element, so that any potential bindings inside are satisfied, and provides this HTML for further usage. The simplest way to render it, is by using the built-in directive
1 |
ng-transclude |
.
Applying a template to an array of items
Another common scenario is to apply a template to an array of items, i.e. for each item in the array to render some HTML generated from the template with that item as a model. This problem is an implementation of generic template approach described above.
React
As a React component always works with the entire portion of (virtual) HTML it has rendered, changes in the state triggers a re-rendering. The way it tackles the problem from above is by manually iterating over the array and generating another array with React components.
1 2 3 4 5 6 7 8 9 10 11 |
var Hello = React.createClass({ render: function() { var people = this.props.to.map(function(person) { return <h4>{person}</h4>; }); return <div>Hello to {people}</div>; } }); var people = ['Anne', 'John', 'Peter']; React.render(<Hello to={people} />, container); |
Running this piece of code, however, will produce the following warning in the developer console:
The problem is that React needs a way to distinguish which element has changed in the array, if you add or remove elements or shuffle the array. In these scenarios it is advised that you add a
1 |
key |
property to the first React component you render from the array. In our example that would be
1 |
<h4 key={person}> |
. The key itself can be a string or number. The idea is that if you happen to shuffle the array, React will ensure that your elements are shuffled in the real DOM, instead of destroyed and recreated.
Angular
As you may already have noticed how Angular solves problems, there is a directive for this –
1 |
ng-repeat |
. 🙂
1 2 3 4 5 6 |
angular.module('app', []).directive('hello', function() { return { templateUrl: 'hello.html', scope: { to: '=' } }; }); |
1 2 3 4 |
<hello to="{{['Anne', 'John', 'Peter']}}"> <h1>Hello to</h1> <h4 ng-repeat="person in to">{{to}}</h4> </hello> |
One applies the
1 |
ng-repeat |
directive to a HTML element, which will be repeated for every item in the array. Moreover, a new child scope will be created for every item (where you can, for example, access the current index of the array).
Conclusion
In this post we saw how both React and Angular deal with templates and how one can nest different components and still provide reusable logic. In the next post we will discuss the lifecycle of components, i.e. how we can inject custom logic, for example, just before a component is rendered.