Components patterns in Ember.js
--
As an Ember developer, you are probably familiar with components. Thanks to them you can split your code into meaningful parts. Unfortunately, it’s not always obvious how they should look like, and which pattern should you use. This snippet should let you better understand what kind of components patterns you can use to be the best match for your case.
Here are all the components patterns I found in the Ember community, that I think is good to know.
Components patterns
Presentational component
This is the basic type of component. It used for presenting, formatting data. It takes some properties and displays them in a more meaningful way. It shouldn’t be responsible for data fetching or business logic. One good example can be a component for displaying a user’s avatar.
Example definition:
// user-avatar/template.hbs
<img src={{user.avatarUrl}}>
Call API example:
{{user-avatar user=user}}
References:
https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0
https://medium.com/@learnreact/container-components-c0e67432e005
Contextual component
These components can be used as a wrapper eg. for markup. They can act like any other component type. The only difference is that they can pass content inside of the block to a specific place in the component using the {{yield}} statement.
Example definition:
// page-header/template.hbs
<div class="jumbotron">
<h1>Hello, world!</h1>
<p>{{yield}}</p>
</div>
Call API example:
{{#page-header}}
How are you?
{{/page-header}}
References:
https://guides.emberjs.com/release/components/wrapping-content-in-a-component/
Container component
This pattern is used for data manipulation, actions handling and all the business logic around. For example, it can fetch the user and format the content using presentational components. Container component can also act as the contextual component, allowing you to overwrite the content and markup by passing a block.
Example definition:
// user-profile/component.js
import Component from '@ember/component';
import { inject as service } from '@ember/service';
import { computed } from '@ember/object';export default Component.extend({
store: service(), id: null,
user: computed('id', function() {
return id ? this.store.findRecord('user', this.id) : null;
}),
});// user-profile/template.hbs
{{#if hasBlock}}
{{yield user}}
{{else}}
{{user.name}}
{{user-avatar user=user}}
{{/if}}
Call API example:
{{#user-profile id=7 as |user|}}
<h1>{{user.name}}</h1>
{{/user-profile}}
References:
https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0
https://medium.com/@learnreact/container-components-c0e67432e005
Composable component (aka Higher-Order Component)
Its role is to deliver to you meaningful components/parts from which you can build your business logic and features but also serves as a great extension for container components. They should still stay flexible so you can add your own markup or modify behaviors. This kind of components can be used in a top-level template where you define your application layout but can be also used to form another component.
Example definition:
// articles-list/template.hbs
{{yield (hash
variables=(hash
articles=articles
) components=(hash
list=(component "articles-list/list" articles=articles)
pagination=(component "articles-list/pagination")
)
)}}
Call API example:
{{#articles-list articles=user.articles as |articlesList|}}
{{#articlesList.components.list as |list|}}
{{#list.components.item as |article|}}
<h1>{{article.title}}</h1>
<p class="lead">{{article.body}}</p>
{{/list.components.item}}
{{/articlesList.components.list}}{{articlesList.components.pagination}}
{{/articles-list}
References:
https://www.chriskrycho.com/2018/higher-order-components-in-emberjs.html
https://riptutorial.com/ember-js/example/10416/composable-components
https://www.youtube.com/watch?v=6N4qsO22fmw
More about composable components soon in next articles :)
Of course, this is just an outline of these patterns but the point is to give an overview of which components pattern we can use during the development process of our application.
Any questions?
Twitter: theMacsour