loading...
Cover image for Applying the open-closed principle to UI components

Applying the open-closed principle to UI components

learnitmyway profile image David Originally published at learnitmyway.com ・3 min read

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

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>

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.

Posted on Jun 1 by:

learnitmyway profile

David

@learnitmyway

I write about my professional and educational experiences as a self-taught software developer.

Discussion

markdown guide