Angular Elements in Non-Angular Apps: Use Angular components in React and VueJS environments
Introduction
An approach to reuse logic and UI created as a component without knowing the whole logic, helps in managing projects. Using this we can seamlessly add in non-Angular environments like React and Vue.js. It is a powerful solution to reduce re-writing code in various projects. So this can be used in today’s fast-evolving frontend ecosystem.
Prerequisites
Before we begin, ensure Node.js and Angular cli is installed.
Step 1: Create a new angular application
Run the following command to generate a new Angular app:
ng new custom-angular --no-standalone --no-routing
cd custom-angular
Note: During setup, choose the default configuration (no SSR, CSS for styling, unless otherwise needed, --no-routing is used to skip the routing configuration and --no-standalone is used to create app module).
After creation serve the project using command
npm start or ng serve
Step 2: Add Bootstrap CDN Links
Add Bootstrap v5.3 using cdn links add these tags to attach css and javascript bundle files in index.html.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>CustomAngular</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-LN+7fdVzj6u52u30Kp6M/trliBMCMKTyK833zpbD+pXdCLuTusPj697FH4R/5mcr" crossorigin="anonymous">
</head>
<body>
<app-root></app-root>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/js/bootstrap.bundle.min.js" integrity="sha384-ndDqU0Gzau9qJ1lfW4pNLlhNTkCfHzAVBReH9diLvGRem5+R9g2FzA8ZGN954O5Q" crossorigin="anonymous"></script>
</body>
</html>
Step 3: Remove the Default App Content and create popup box
By default, Angular includes sample HTML in the AppComponent. To prepare for a custom popup element.
Additions in app.component.html
<div class="bg"></div>
<div class="card mx-auto mt-5" style="width: 28rem;">
<div class="card-body">
<div>
<h5 class="card-title">{{title}}</h5>
<p class="card-text">{{message}}</p>
</div>
<div class="text-end">
<button class="btn btn-danger me-2" (click)="close.emit()">{{back}}</button>
<button class="btn btn-success" (click)="submit.emit()">{{proceed}}</button>
</div>
</div>
</div>
Additions in app.component.css
.bg{
height: 100vh;
width: 100vw;
background-color: rgb(0, 0, 0);
opacity: 0.3;
position: fixed;
top: 0;
}
Additions in app.component.ts
@Input() title: any
@Input() message: any
@Input() back: any
@Input() proceed: any
@Output() close = new EventEmitter()
@Output() submit = new EventEmitter()
Add this code in the app component to create a popup element and variables title, message, back and proceed are created to show in the popup box.
Step 4: Install angular elements
Use Angular CLI commands to install angular elements package. Open a terminal in your project directory and run the following:
Run ‘npm i @angular/elements@18.2.13’
Step 5: Configure App Module
Update your app.module.ts
file to define custom elements. This includes default declarations and imports. Add a constructor in AppModule.
import { Injector, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { createCustomElement } from '@angular/elements';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule
]
})
export class AppModule {
constructor(private injector:Injector){
const popupBox = createCustomElement(AppComponent, {injector})
customElements.define('popup-box', popupBox)
}
ngDoBootstrap(){}
}
Now add the popup-box tag in index.html.
Modifications in index.html
<body>
<popup-box message="Do you want to delete?" title="Delete confirmation" back="Cancel" proceed="Yes, Delete it?" (close)="close()" (submit)="submit()"></popup-box>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/js/bootstrap.bundle.min.js" integrity="sha384-ndDqU0Gzau9qJ1lfW4pNLlhNTkCfHzAVBReH9diLvGRem5+R9g2FzA8ZGN954O5Q" crossorigin="anonymous"></script>
</body>
The popup element looks like:
Step 6: Create build of Angular project.
Create the build of angular project using the command
Run ‘ng build -configuration production’
This will create a dist folder in the app directory and inside its dist-> custom-angular -> browser folder main.js and polyfills.js will be created to be used in Reactjs and VueJs projects as mentioned below.
Step 7: Setup of React Project
Create a new react app to implement angular elements in React. Using Vite create a new react app by command
Run ‘npm create vite’
After running this command choose yes to proceed, add project name, select framework as React, variant as Javascript and run the commands provided after it.
Run the commands to proceed further
Run ‘cd custom-reactjs’
Run ‘npm install’
Run ‘npm run dev’
Remove the styling from index.css
and App.css.
Also remove the default design from App.jsx.
Step 8: Add Bootstrap CDN Links and main and polyfills in react app
Add Bootstrap v5.3 using CDN links add these tags to attach css and javascript bundle files, and main.js and polyfills.js from angular dist. Create a folder named ang in the public folder and add the files in it.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-LN+7fdVzj6u52u30Kp6M/trliBMCMKTyK833zpbD+pXdCLuTusPj697FH4R/5mcr" crossorigin="anonymous">
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/js/bootstrap.bundle.min.js" integrity="sha384-ndDqU0Gzau9qJ1lfW4pNLlhNTkCfHzAVBReH9diLvGRem5+R9g2FzA8ZGN954O5Q"
crossorigin="anonymous"></script>
<script type="module" src="/public/ang/main-ISKHZCKT.js"></script>
<script src="/public/ang/polyfills-FFHMD2TL.js"></script>
</body>
</html>
Step 9: Creating a sample in Layout component
Now let’s start working on the layout component. Create a Layout.jsx and the following code in it.
On click on the button open modal by showing and on click of close and submit hide the popup again.
Additions in Layout.jsx
import { useRef, useState } from "react";
function Layout() {
const [isModelOpen, SetModelStatus] = useState(false);
const popRef = useRef();
const closeFun = () => {
SetModelStatus(false);
}
const openFun = () => {
SetModelStatus(true);
setTimeout(() => {
let elCurrent = popRef.current
if (elCurrent) {
elCurrent.addEventListener('close', closeFun);
elCurrent.addEventListener('submit', closeFun);
}
}, 100)
}
return (
<>
<div className="py-5 container">
<button className="btn btn-primary" onClick={openFun}>Open Popup</button>
</div>
{isModelOpen ?
<popup-box message={"Do you want to delete?"} title={"Delete confirmation"} back={"Cancel"} proceed={"Yes, Delete it?"} ref={popRef}></popup-box>
: ''}
</>
)
}
export default Layout;
Modifications in App.jsx
import './App.css';
import Layout from './Layout';
function App() {
return (
<>
<Layout />
</>
)
};
export default App;
The output looks like
Step 10: Setup of VueJs Project
Create a new vue.js app to implement angular elements in VueJs. Using Vite create a new vue app by command
Run ‘npm create vite’
After running this command choose yes to proceed, add project name, select framework as Vue, variant as Javascript and run the commands provided after it.
Run the commands to proceed further
Run ‘cd custom-vuejs’
Run ‘npm install’
Run ‘npm run dev’
Remove the styling from style.css. Also remove the default design from App.vue.
Step 11: Add Bootstrap CDN Links and main and polyfills in vue js app
Add Bootstrap v5.3 using CDN links add these tags to attach css and javascript bundle files, and main.js and polyfills.js from angular dist. Create a folder named ang in the public folder and add the files in it.
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-LN+7fdVzj6u52u30Kp6M/trliBMCMKTyK833zpbD+pXdCLuTusPj697FH4R/5mcr" crossorigin="anonymous">
</head>
<body>
<div id="app"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/js/bootstrap.bundle.min.js" integrity="sha384-ndDqU0Gzau9qJ1lfW4pNLlhNTkCfHzAVBReH9diLvGRem5+R9g2FzA8ZGN954O5Q" crossorigin="anonymous"></script>
<script type="module" src="/src/main.js"></script>
<script type="module" src="/public/ang/main-ISKHZCKT.js"></script>
<script src="/public/ang/polyfills-FFHMD2TL.js"></script>
</body>
</html>
Step 12: Creating a sample in Layout component
Now let’s start working on the layout component. Create a Layout.vue in components folder and the following code in it.
On click on the button open modal by showing and on click of close and submit hide the popup again.
Additions in Layout.vue
<script setup>
import {ref, nextTick} from 'vue'
const isModelOpen = ref(false)
const popRef = ref(null)
function change(){
isModelOpen.value = !isModelOpen.value
nextTick(() => {
if(isModelOpen.value){
popRef.value.addEventListener('close',change);
popRef.value.addEventListener('submit',change);
}
})
}
</script>
<template>
<div class="py-5 container">
<button class="btn btn-primary" @click="change()">Open Popup</button>
</div>
<popup-box v-if="isModelOpen" message="Do you want to delete?" title="Delete confirmation" back="Cancel" proceed="Yes, Delete it?" ref="popRef"></popup-box>
</template>
<style scoped>
</style>
Modifications in App.vue
<script setup>
import Layout from './components/Layout.vue';
</script>
<template>
<Layout/>
</template>
The output looks like
Conclusion :
This unlocks a new feature to collaborate across framework boundaries without affecting performance. To export a standard web component and use angular for that, we can achieve a framework based component model to reduce efforts in development. To ensure the consistency in UI it is important to load angular scripts correctly, handling event bindings which provides reusability in frontend frameworks.