{"id":2226,"date":"2022-09-20T22:00:11","date_gmt":"2022-09-20T20:00:11","guid":{"rendered":"https:\/\/nguenkam.com\/blog\/?p=2226"},"modified":"2022-09-20T22:16:34","modified_gmt":"2022-09-20T20:16:34","slug":"angular-smart-vs-dumb-components","status":"publish","type":"post","link":"https:\/\/nguenkam.com\/blog\/index.php\/2022\/09\/20\/angular-smart-vs-dumb-components\/","title":{"rendered":"Angular \u2013 Smart vs Dumb Components"},"content":{"rendered":"\n<p>When we start building apps with UI frameworks like Angular, the most frequent questions that we are faced with right at the beginning is: how do we structure our application? <\/p>\n\n\n\n<ul><li>what types of components are there?<\/li><li>how should components interact?<\/li><li>should I inject services into any component?<\/li><li>how do I make my components reusable across views?<\/li><\/ul>\n\n\n\n<p><em>A very common concept is the splitting of Angular Components into <strong>Smart <\/strong>and <strong>Dumb<\/strong> Components ( also know sometimes as<strong> container<\/strong> components  and &nbsp;<strong>Presentation<\/strong> Components).<\/em><\/p>\n\n\n\n<h4>Smart VS Dumb Component<\/h4>\n\n\n\n<p>Let&#8217;s find out the differences between these two types of components, and when should we use each and why!<\/p>\n\n\n\n<ul><li>a smart component can have external dependencies (Dependencies Injection) and may trigger side effects (e.g. API calls)<\/li><li>a Dumb Component has no external dependencies and produces no side effects. Communication with the parent component should only be made trough @Input() and @Output() decorators.<\/li><\/ul>\n\n\n\n<h4>Example<\/h4>\n\n\n\n<p>In order to understand the difference between the two types of components, let&#8217;s start with a simple application, where the separation is not yet present.<\/p>\n\n\n\n<p>We started building the Home screen of an application and we have added multiple features to a single template:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/home.ts\n\n@Component({\n  selector: 'app-home',\n  template: `\n    &lt;h2&gt;All Lessons&lt;\/h2&gt;\n    &lt;h4&gt;Total Lessons: {{lessons?.length}}&lt;\/h4&gt;\n    &lt;div&gt;\n        &lt;table class=\"table lessons-list card card-strong\"&gt;\n            &lt;tbody&gt;\n            &lt;tr *ngFor=\"let lesson of lessons\" (click)=\"selectLesson(lesson)\"&gt;\n                &lt;td class=\"lesson-title\"&gt; {{lesson.description}} &lt;\/td&gt;\n                &lt;td class=\"duration\"&gt;\n                    &lt;i class=\"md-icon duration-icon\"&gt;access_time&lt;\/i&gt;\n                    &lt;span&gt;{{lesson.duration}}&lt;\/span&gt;\n                &lt;\/td&gt;\n            &lt;\/tr&gt;\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n    &lt;\/div&gt;\n`,\n  styleUrls: &#91;'.\/home.component.css']\n})\nexport class HomeComponent implements OnInit {\n\n    lessons: Lesson&#91;];\n\n  constructor(private lessonsService: LessonsService) {\n  }\n\n  ngOnInit() {\n      this.lessonsService.findAllLessons()\n          .pipe(\n              tap(console.log)\n           )\n          .subscribe(\n              lessons =&gt; this.lessons = lessons\n          );\n  }\n\n  selectLesson(lesson) {\n    ...\n  }\n\n}<\/code><\/pre>\n\n\n\n<h4><span class=\"has-inline-color has-luminous-vivid-orange-color\">Understanding the Problem<\/span><\/h4>\n\n\n\n<p>Although this Homepage component is still very simple, it&#8217;s already starting to have a significant size. For example, we have implemented a table containing a list of lessons.<\/p>\n\n\n\n<p>But there will likely be other parts of the application where this functionality is also needed, for example let&#8217;s say that we have another screen which presents the table of contents of a given course.<\/p>\n\n\n\n<p>In that screen we also want to display a list of lessons, but only the lessons that belong to that course. In that case, we would need something very similar to what we have implemented in the Home screen.<\/p>\n\n\n\n<p>And we shouldn&#8217;t just copy-paste this across components, we should create a reusable component, right?<\/p>\n\n\n\n<h4><span class=\"has-inline-color has-vivid-cyan-blue-color\">Let&#8217;s create a Presentation Component<\/span><\/h4>\n\n\n\n<p>What we will want to do in this situation is to extract the table part of the screen into a separate component, let&#8217;s call it the&nbsp;<code>LessonsListComponent<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/lesson-list.ts\nimport {Component, OnInit, Input, EventEmitter, Output} from '@angular\/core';\nimport {Lesson} from \"..\/shared\/model\/lesson\";\n\n@Component({\n  selector: 'lessons-list',\n  template: `\n      &lt;table class=\"table lessons-list card card-strong\"&gt;\n          &lt;tbody&gt;\n          &lt;tr *ngFor=\"let lesson of lessons\" (click)=\"selectLesson(lesson)\"&gt;\n              &lt;td class=\"lesson-title\"&gt; {{lesson.description}} &lt;\/td&gt;\n              &lt;td class=\"duration\"&gt;\n                  &lt;i class=\"md-icon duration-icon\"&gt;access_time&lt;\/i&gt;\n                  &lt;span&gt;{{lesson.duration}}&lt;\/span&gt;\n              &lt;\/td&gt;\n          &lt;\/tr&gt;\n          &lt;\/tbody&gt;\n      &lt;\/table&gt;  \n  `,\n  styleUrls: &#91;'.\/lessons-list.component.css']\n})\nexport class LessonsListComponent {\n\n  @Input()\n  lessons: Lesson&#91;];\n\n  @Output('lesson')\n  lessonEmitter = new EventEmitter&lt;Lesson&gt;();\n\n    selectLesson(lesson:Lesson) {\n        this.lessonEmitter.emit(lesson);\n    }\n\n}<\/code><\/pre>\n\n\n\n<p>Now let&#8217;s take a closer look at this component: it does not have the lessons service injected into it via its constructor. Instead, it receives the lessons in an input property via&nbsp;<code>@Input<\/code>.<\/p>\n\n\n\n<p>This means that the component itself does not know where the lessons come from:<\/p>\n\n\n\n<ul><li>the lessons might be a list of all lessons available<\/li><li>or the lessons might be a list of all the lessons of a given course<\/li><li>or even the lessons might be a page in any given list of a search<\/li><\/ul>\n\n\n\n<p>We could reuse this component in all of these scenarios, because the lessons list component does not know where the data comes from. The responsibility of the component is purely to present the data to the user and not to fetch it from a particular location.<\/p>\n\n\n\n<p><em><span class=\"has-inline-color has-vivid-cyan-blue-color\">This is why we usually call to this type of component a<strong> Presentation <\/strong>or <strong>Dumb<\/strong> Component.<\/span><\/em><\/p>\n\n\n\n<h4><span class=\"has-inline-color has-vivid-cyan-blue-color\">Let&#8217;s create a Smart Component<\/span><\/h4>\n\n\n\n<p>what happened to our Home Component? This is what it will look like after refactoring:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/home.ts\n\nimport { Component, OnInit } from '@angular\/core';\nimport {LessonsService} from \"..\/shared\/model\/lessons.service\";\nimport {Lesson} from \"..\/shared\/model\/lesson\";\n\n@Component({\n  selector: 'app-home',\n  template: `\n      &lt;h2&gt;All Lessons&lt;\/h2&gt;\n      &lt;h4&gt;Total Lessons: {{lessons?.length}}&lt;\/h4&gt;\n      &lt;div&gt;\n          &lt;lessons-list &#91;lessons]=\"lessons\" (lesson)=\"selectLesson($event)\"&gt;&lt;\/lessons-list&gt;\n      &lt;\/div&gt;\n`,\n  styleUrls: &#91;'.\/home.component.css']\n})\nexport class HomeComponent implements OnInit {\n\n    lessons: Lesson&#91;];\n\n  constructor(private lessonsService: LessonsService) {\n  }\n\n  ngOnInit() {\n     ...\n  }\n\n  selectLesson(lesson) {\n    ...\n  }\n\n}<\/code><\/pre>\n\n\n\n<p>As we can see, we have replaced the list part of the Home screen with our new reusable lessons list component. The home component still knows how to retrieve the lessons list from a service, and what type of list this is (if these lessons are the lessons of a course, etc.).<\/p>\n\n\n\n<p>But the Home component does not know how to&nbsp;<em>present<\/em>\/display&nbsp;the lessons to the user.<\/p>\n\n\n\n<p>This type of component is inherently tied to the application itself, so as we can see it receives in the constructor some dependencies that are application-specific, like the&nbsp;<code>LessonsService<\/code>.<\/p>\n\n\n\n<p><strong>PS: <\/strong><em><span class=\"has-inline-color has-vivid-cyan-blue-color\">It would be very hard to use this component in another application.<\/span><\/em><\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p><\/p>\n\n\n\n<h4>Summary<\/h4>\n\n\n\n<p>Smart vs Presentation Components is more of a mindset to adopt where we ask ourselves:<\/p>\n\n\n\n<ul><li>would this presentation logic be useful elsewhere in the application?<\/li><li>would it be useful to split things up further?<\/li><li>are we creating unintended tight couplings in the application?<\/li><\/ul>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h5>Reference:<\/h5>\n\n\n\n<p><a href=\"https:\/\/blog.angular-university.io\/\">https:\/\/blog.angular-university.io\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>When we start building apps with UI frameworks like Angular, the most frequent questions that we are faced with right at the beginning is: how do we structure our application? what types of components are there? how should components interact? should I inject services into any component? how do I make my components reusable across [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":2227,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[37],"tags":[601,292,182,600,602,599],"_links":{"self":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/2226"}],"collection":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=2226"}],"version-history":[{"count":2,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/2226\/revisions"}],"predecessor-version":[{"id":2229,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/2226\/revisions\/2229"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/media\/2227"}],"wp:attachment":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=2226"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=2226"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=2226"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}