SOLID Principles: Angular
S – Single Responsibility
Ex: A
component cannot have multiple widgets which can instead be made as a reusable component
for future.
|
Instead of |
Do this |
|
app.component.ts <div> <!--Navbar -->
<nav>
<ul>
<li>Savings</li>
<li>Deposits</li>
<li>Loan</li>
</ul>
</nav> <!--card
to display savings account details -->
<div class="card-wrapper">
<div class="header">CITI BANK</div>
Account: AC29326262
Balance: 5000
Transactions avg: 3 </div>
</div> <!--card to display deposits details
-->
<div class="card-wrapper">
<div class="header">CITI BANK</div>
Account: AC29326262
Deposits Total: 90000
No. of Deposits: 6
</div>
</div> </div> |
app.component.ts <nav-bar></nav-bar> <card [type]=”savings”></card> <card [type]=”deposits”></card> navbar.component.ts
<nav>
<ul>
<li>Savings</li>
<li>Deposits</li>
<li>Loan</li>
</ul>
</nav> card.component.ts <div *ngIf=”type == ‘savings’ > <div
class="card-wrapper">
<div class="header">CITI BANK</div>
Account: AC29326262
Balance: 5000
Transactions avg: 3 </div>
</div> <div *ngIf=”type == ‘deposits > <div
class="card-wrapper">
<div class="header">CITI BANK</div>
Account: AC29326262
Deposits Total: 90000
No. of Deposits: 6 </div>
</div> |
O- Open for extension,
closed for modification
All the libraries are already good example of this as we only extend the
use in out app but we do not have the control to the source file of the library.
Above example is closed for modification, yes. Only savings and deposits type
can be displayed. But not open for extension.
The savings and deposits cards should now be made as separate components.
Card component has to contain the header and <ng-content>
Savings and deposits components should use card component to fill the
details in <ng-content>
App.component.ts will have savings and deposits components
Now having just <card></card> will still display the
structure of the card with empty content. It is now available for extension: <card>
Test </card>
Card.component.ts
<div class="card-wrapper">
<div
class="header">CITI BANK</div>
<ng-content></ng-content>
</div>
|
App.component.html |
Deposit.component.ts |
Savings.component.ts |
|
<savings></savings> <deposits></deposits> <card> test </card> |
<card>
Account: AC29326262
Deposits Total: 90000
No. of Deposits: 6 </card> |
<card>
Account: AC29326262
Balance: 5000
Transactions avg: 3 </card> |
Output:
L – Liskov Substitution
Consider we have export to csv/download button in the content of both
savings and deposits component. This is now redundant code. Let us have a separate
download directive. This download directive now acts as a parent/base class
Parent/base component
|
Download.component.ts |
|
@Directive({ selector: 'download'
}) export class DownloadComponent {
download() {
alert('Downloading in parent class...'); } } |
Now if savings component extends this base directive and also has the
same ‘download()’ method, it is a violation of Liskov’s principle.
|
WRONG |
RIGHT |
|
Savings.component.ts <button (click)=”download()”>
Download <button> export class SavingsCardComponent
extends DownloadComponent {
download() {
alert('I do not support it...');
} } Output: ‘I do not support it’ |
Savings.component.ts <button (click)=”download()”>
Download <button> export class SavingsCardComponent
extends DownloadComponent {
download() {
super.download();
alert('I do not support it...');
} } Output: ‘Downloading in parent
class’
‘I do not support
it’ |
Note: Implementation of the same function of the parent class should not
be replaced.
I – Interface Segregation Service:
Many client-specific interfaces are better than one general-purpose interface
One big interface is split into smaller interfaces as few properties
might not be valid for all components that implement it
Ex: export interface ICard {
Title:string,
isLoading:
Boolean,
interest:
number
}
Now, the deposits component needs to calculate interest but not savings
component.
|
Savings.component.ts |
Deposits.component.ts |
|
export class savings implements ICard { Title=’’; isLoading=false; interest=0; //not
needed. Still need to have atleast default as ICard is “implemented” } |
export class savings implements ICard { Title=’’; isLoading=false; interest=6; } |
So have another Interface:
|
export interface ICard { amount:number, isLoading:
Boolean, interest:
number } |
export interface ICard { amount:number, } export interface ICardInterest { isLoading:
Boolean, interest:
number } |
And import
each of it separately where needed:
|
Savings.component.ts |
Deposits.component.ts |
|
export class savings implements ICard { amount=82323; isLoading=false; interest=0; //not
needed. Still need to have atleast default as ICard is “implemented” } |
export class savings implements ICard, ICardInterest { amount=3737; isLoading=false; interest=6; } |
D - Dependency Inversion
Principle: High level modules should not depend on low-level modules. It should
depend upon abstraction
This has to
be kept in mind every time Dependency Injection is used.
Consider you
need to do some calculation in card component where <ng-content> is
present based on whether the “interest” is present in that component or not. In
card component you can use ‘@ContentChild()’ to load the deposits/savings
component and check if “interest” property is present on not.
export class CardComponent {
@Input() type;
@ContentChild(DepositsCardComponent)
depositCmp?: DepositsCardComponent;
ngAfterContentInit() {
alert(this.depositCmp);
if (this.depositCmp) {
alert('interest
present....');
}
}
}
This is the violation of rule because there is a hard dependency on the
component name directly. What we are interested in is any component in the
ng-content that has the “interest” property.
So create abstraction:
StackBlitz Reference: https://angular-ivy-pguqxi.stackblitz.io
Comments
Post a Comment