In this article, I will demonstrate a simple example of applying the open/closed principle to a UI component in React or Angular.
Background
I had an aha moment this week regarding the open/closed principle, which states “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification” and is the O in SOLID. I have always found this principle to be quite abstract and I didn’t know if I was applying it until now.
The aha moment came to me when I wanted to change the style of an existing component. For simplicity’s sake, let’s say this was a button and I wanted to change the existing background colour. Let’s see how this works in React and then Angular. Or you can skip straight to Angular.
React
Link to source code.
I will start with a simple button component:
// src/Button.js
import React from 'react'
import './Button.css'
const Button = () => (
<button className="Button" type="button">
Click Me!
</button>
)
export default Button
that has the background color aliceblue
:
/* src/Button.css */
.Button {
background-color: aliceblue;
}
and is used as follows:
// src/App.js
import React from 'react'
import './App.css'
import Button from './Button'
const App = () => (
<div className="App">
<Button />
</div>
)
export default App
Now our app’s stakeholders have said they would like us to add a new button in papayawhip
directly beside the existing button. They have also said there are more buttons to follow. So I parameterise the className
in the Button
component:
// src/Button.js
import React from 'react'
const Button = ({ className }) => (
<button className={className} type="button">
Click Me!
</button>
)
export default Button
and then use it as follows:
// src/App.js
import React from 'react'
import './App.css'
import Button from './Button'
const App = () => (
<div className="App">
<Button className="button-aliceblue" />
<Button className="button-papayawhip" />
</div>
)
export default App
From now on, by passing className
to Button
as a prop I can update (extend) the styles without changing (modifying) the Button
component:
/* src/App.css */
.button-aliceblue {
background-color: aliceblue;
}
.button-papayawhip {
background-color: papayawhip;
}
This just fulfilled the open/closed principle!
CSS-in-React
A similar approach can also be used with styled-components.
Angular
Link to source code.
I will start with a simple button component:
// src/app/button/button.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-button',
templateUrl: './button.component.html',
styleUrls: ['./button.component.css'],
})
export class ButtonComponent {}
<!-- src/app/button/button.component.html -->
<button class="button" type="button">Click me!</button>
that has the background color aliceblue
:
/* src/app/button/button.component.css */
.button {
background-color: aliceblue;
}
and is used as follows:
<!-- src/app/app.component.html -->
<div class="content" role="main">
<app-button></app-button>
</div>
Now our app’s stakeholders have said they would like us to add a new button in papayawhip
directly beside the existing button. They have also said there are more buttons to follow. So I parameterise the style of the Button
component (I would have preferred to parameterise the CSS class name, like in the React example above, but I couldn’t figure out how to do it):
// src/app/button/button.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-button',
templateUrl: './button.component.html',
styleUrls: ['./button.component.css'],
})
export class ButtonComponent {
@Input() style: { [index: string]: string };
}
and then use it as follows:
<!-- src/app/button/button.component.html -->
<button [ngStyle]="style" type="button">Click me!</button>
From now on, by passing style
to app-button
as a property I can add a button and update (extend) the styles without changing (modifying) the app-button
component:
<!-- src/app/app.component.html -->
<div class="content" role="main">
<app-button
[style]="{
backgroundColor: 'aliceblue'
}"
></app-button>
<app-button
[style]="{
backgroundColor: 'papayawhip'
}"
></app-button>
</div>
This just fulfilled the open/closed principle!
Final Thoughts
I hope this simple example has helped you understand how the open/closed principle can be applied to UI components. Feel free to look at the source code in React or Angular.