Understanding Smart and Dumb Components in Angular

Angular Components

In this blog, we will see smart components and dumb components in Angular, why you should use this pattern in your app, and the execution or implementation of smart and dumb components. Angular has strong and extraordinary features because of the frequent and timely updates. To talk about Smart Components and Dumb Components, they do not have anything to do it with Angular, it is just an approach we can use in any framework or library like react, Vue, etc.

The component structure will look like the following:

Angular Components

We will divide our Chat component into three parts.

Angular Components

Dumb Component

Dumb components are the components that does not have a bunch of long-lived states. It does not have access to services nor have stored data in a back-end, all kinds of things that actually keep the focus of the component on presentation. We can also call them Isolated Components or Pure Components. Dumb or child component is only showing the data got from Smart Component.

my-chat.service.ts

import { Injectable } from ‘@angular/core’;

import { UserMessage } from ‘src/app/mychat.model’;

import { YtChannel } from ‘src/app/yt-channel.model’;

@Injectable()

export class MyChatService {

  private channelsDb: YtChannel[] = [

    { _id: ‘0’, name: ‘general’ },

    { _id: ‘1’, name: ‘channel1’ }

  ];

  // fake database

  private messagesDb: UserMessage[] = [

    { _id: ‘0’, YtchannelId: ‘0’, userId: ‘0’, userName: ‘Manoj’, content: ‘Hello!’, timestamp: new Date() },

    { _id: ‘1’, YtchannelId: ‘1’, userId: ‘0’, userName: ‘Ankit’, content: ‘Hi!’, timestamp: new Date() },

    { _id: ‘2’, YtchannelId: ‘0’, userId: ‘0’, userName: ‘Jignesh’, content: ‘Hi! Good Morning’, timestamp: new Date() }

  ];

  channels: YtChannel[] = [];

  messages: UserMessage[] = [];

  channel: YtChannel;

  getYtChannels(): void {

    // fake API, get your ytchannel data from the database

    this.channels = this.channelsDb;

    this.channel = this.channels[0];

  }

  getYourMessages(channelName: string): void {

    // fake API, get your message data from the database

    let channelId = ”;

    this.channelsDb.map(channel => {

      if (channel.name === channelName) {

        channelId = channel._id;

      }

    });

    const messages = [];

    this.messagesDb.map(message => {

      if (message.YtchannelId === channelId) {

        messages.push(message);

      }

    });

    this.messages = messages;

  }

  selectYtChannel(channel: YtChannel): void {

    this.channel = channel;

    this.getYourMessages(channel.name);

  }

  sendYourMessage(channelId: string, messageContent: string): void {

    const message = {

      _id: String(this.messagesDb.length),

      channelId,

      userId: ‘0’,

      userName: ‘Manoj’,

      content: messageContent,

      timestamp: new Date()

    };

    this.messages = […this.messages, message];

    this.messages = […this.messages, message];

    // fake API, write message data to the database

    this.messagesDb = […this.messagesDb, message];

  }

  constructor() { }

}

my-chat-area-bottom.component.ts

import { Component, EventEmitter, Input, OnInit, Output } from ‘@angular/core’;

import { FormBuilder, FormGroup, Validators } from ‘@angular/forms’;

@Component({

  selector: ‘app-my-chat-area-bottom’,

  templateUrl: ‘./my-chat-area-bottom.component.html’,

  styleUrls: [‘./my-chat-area-bottom.component.css’]

})

export class MyChatAreaBottomComponent implements OnInit {

  @Input() ytChannelId: string;

  @Output() sendYourMessage = new EventEmitter<any>();

  messageForm: FormGroup;

  constructor(

    private fb: FormBuilder

  ) { }

  ngOnInit() {

    this.messageForm = this.fb.group({

      message: [”, Validators.required]

    });

  }

  onSendMessage() {

    if (!this.messageForm.valid) return;

    this.sendYourMessage.emit({ channelId: this.ytChannelId, messageContent: this.messageForm.value.message });

    this.messageForm.reset();

  }

}

my-chat-area-bottom.component.html

<div class=”wrapper”>

    <form [formGroup]=”messageForm” (ngSubmit)=”onSendYourMessage()”>

        <input formControlName=”message” placeholder=”Click on enter to send message” />

    </form>

</div>

If you verify the above code snippet, there is nothing new excepting @input () and @output ().

 @Input() ytChannelId: string;

 @Output() sendYourMessage = new EventEmitter<any>();

ytChannelId and sendYourMessage are equivalent to the values

<my-chat-area-bottom>

    [YtChannelId]=”mychatService.ytchannel?._id”

    (sendYourMessage)=”onSendYourMessage($event)”>

</my-chat-area-bottom>

Now you need to be clear about how they equivalent to each other.

Observe that, sendYourMessage is an EventEmitter, which can help us to release the event to the parent component in our scenario, it excludes the event with an object to the parent component.

this.sendYourMessage.emit({ channelId: this.ytChannelId, messageContent: this.messageForm.value.message });

ytchannel-list-component.ts

import { Component, EventEmitter, Input, OnInit, Output } from ‘@angular/core’;

import { YtChannel } from ‘src/app/yt-channel.model’;

@Component({

  selector: ‘app-ytchannel-list’,

  templateUrl: ‘./ytchannel-list.component.html’,

  styleUrls: [‘./ytchannel-list.component.css’]

})

export class YtchannelListComponent implements OnInit {

  constructor() { }

  @Input() channels: YtChannel[];

  @Input() ytChannelId: string;

  @Output() selectYtChannel = new EventEmitter<YtChannel>();

  onSelectYtChannel(channel: YtChannel) {

    this.selectYtChannel.emit(channel);

  }

  ngOnInit(): void {

  }

}

ytchannel-list-component.html

<ul>

    <li *ngFor=”let channel of channels” [ngClass]=”{‘selected’: ytchannel._id === ytChannelId}”

        (click)=”onSelectYtChannel(channel)”>

        {{channel.name}}

    </li>

</ul>

my-chat-area-dialog-component.ts

import { Component, Input, OnInit } from ‘@angular/core’;

import { UserMessage } from ‘..//mychat.model’;

@Component({

  selector: ‘app-my-chat-area-dialog’,

  templateUrl: ‘./my-chat-area-dialog.component.html’,

  styleUrls: [‘./my-chat-area-dialog.component.css’]

})

export class MyChatAreaDialogComponent implements OnInit {

  @Input() messages: UserMessage[];

  constructor() { }

  ngOnInit(): void {

  }

}

my-chat-area-dialog-component.html

<ul class=”wrapper”>

    <li *ngFor=”let message of messages”>

        <p><span class=”name”>{{message.userName}}</span> <span

                class=”timestamp”>{{message.timestamp | calendar}}</span></p>

        <p>{{message.content}}</p>

    </li>

</ul>

Smart Component

  • Smart Components are only those components, which have states, implementing services, or any kind of business logic occurrence in that component. It does not attend on presentation but what is occurring in our component. Then we easily emit the property to Dumb Components which are obtained by @input decorator.
  • We can also call them Application-level Components or Container Components.
  • A smart or present component is responsible to communicate with the server or cloud through the Service.
  • The MyChatComponent is the first smart one or parent component 

my-chat.component.ts

import { Component, OnInit } from ‘@angular/core’;

import { MyChatService } from ‘src/app/my-chat.service’;

import { YtChannel } from ‘src/app/yt-channel.model’;

@Component({

  selector: ‘app-my-chat’,

  templateUrl: ‘./my-chat.component.html’,

  styleUrls: [‘./my-chat.component.css’]

})

export class MyChatComponent implements OnInit {

  constructor(

    private mychatService: MyChatService

  ) { }

  ngOnInit() {

    this.mychatService.getYtChannels();

    this.mychatService.getYourMessages(‘general’);

  }

  onSelectYtChannel(ytchannel: YtChannel) {

    this.mychatService.selectYtChannel(ytchannel);

  }

  onSendYourMessage({ YtChannelId, messageContent }) {

    this.mychatService.sendYourMessage(YtChannelId, messageContent);

  }

}

my-chat.component.html

<div class=”left”>

    <my-channel-list [ytchannels]=”mychatService.channels” [ytchannelId]=”mychatService.ytchannel?._id”

        (selectYtChannel)=”onSelectYtChannel($event)”>

    </my-channel-list>

</div>

<div class=”right”>

    <my-chat-area-dialog [messages]=”mychatService.messages”>

    </my-chat-area-dialog>

    <my-chat-area-bottom [YtChannelId]=”mychatService.ytchannel?._id” (sendYourMessage)=”onSendYourMessage($event)”>

    </my-chat-area-bottom>

</div>

Let we use <my-chat-area-bottom> to define how smart or parent component and dumb or child component communicate:

<my-chat-area-bottom>

    [YtChannelId]=”mychatService.ytchannel?._id”

    (sendYourMessage)=”onSendYourMessage($event)”>

</my-chat-area-bottom>

Property Binding

[YtChannelId]=”mychatService.ytchannel?._id”

  • Whenever we want to transfer or communicate data from parent component to a child component, we can use Property Binding.
  • In our scenario, YtChannelId it is the property name and mychatService.ytchannel? _id is the data we want to transfer to the child component.

Event Binding

(sendYourMessage)=”onSendYourMessage($event)”

  • Whenever we want to pass or send some data from the child component to the parent component, we can use Event Binding.

Following are the benefits of using Smart Component approaches:

  • Smart Components are easily shared throughout our application if it configured correctly.
  • We can believe that these things are all heading off in isolation.
  • Have a Stable Experience.
  • They are going to help you to architect and scale your application.

Conclusion

In this blog, we have seen Angular Smart and Dumb Component Architecture in which we have defined the Smart and Dumb Component with the proper example and explanation of it. We have also seen the main benefits of using a Smart component.

Leave a Reply

Your email address will not be published. Required fields are marked *