Angular is one of the ways of creating a nice front-end web application, or even a mobile app if you want. But sometimes it’s just massive and you want to use one small part of your website’s Angular functionality, such as WordPress. Angular Elements can help you with that; It allows you to create a piece of Angular functionality that you can embed in your website. This article will tell you how.
Note: If you want to follow this article to the letter you will need an API key for The MovieDB. With this API key, we can retrieve movie information, as will be shown in this article.
Table Of Contents
The Goal
The goal is pretty simple: Create a custom Angular piece with Angular Elements. And to give you an idea about what the end goal is…. Look below:
Refresh the page a few times and see that the movie is changing. The movie (title, poster, and description) is loaded through Angular Elements; a framework of Angular that makes it possible to embed a small piece of Angular inside another website.
Do note that you still need NodeJS to make it work. You can’t Angular Elements does not work without it. You will also need a dedicated web server or some kind, such as Wampserver64. JavaScript files are needed to include and they don’t work when you load a HTML file directly. This is due to security reasons.
This article tells you how you can create this custom Angular Element and use it in your own website, such as WordPress.
You can find the whole application, with working code, on GitHub.
Create The Application
The first thing we need to do is create the Angular application. I will call my project MovieRotator, which will be a standalone project, so no other parameters are needed. For the styling, I will be using SCSS and I don’t want to enable SSG/Prerendering.
ng new MovieRotator
You will get a standard Angular application with a lot of files we won’t be using, but let’s not delete them just yet.
The Component
First I want to make sure the whole Angular app works as a whole and inside the website itself. That means I won’t convert it to an element just yet. Let’s make sure everything works before converting it.
To add the component, use the following command:
ng generate component MovieRotation
This will create the new component inside the app folder. Move the folder movie-rotator out of the app folder so it is placed in the src folder.
I am not going to explain why and how the code of this component works, that’s not the point of this article. Just know it works, if you copy-paste it right.
The HTML is pretty simple:
<h2>{{ selectedMovie?.title }}</h2> <div class="container"> <div class="poster"> <img src="https://image.tmdb.org/t/p/w500/{{ selectedMovie?.poster_path }}" /> </div> <div class="content"> {{ selectedMovie?.overview }} </div> </div>
The TypeScript looks a bit more interesting:
import { HttpClient, HttpClientModule, HttpHeaders } from '@angular/common/http'; import { Component } from '@angular/core'; @Component({ selector: 'app-movie-rotation', standalone: true, imports: [HttpClientModule], templateUrl: './movie-rotation.component.html', styleUrl: './movie-rotation.component.scss' }) export class MovieRotationComponent { selectedMovie: any = null; constructor(http: HttpClient) { var url = "https://api.themoviedb.org/3/discover/movie?include_adult=false&include_video=false&language=en-US&page=1&sort_by=popularity.desc"; var h = new HttpHeaders(); h = h.append("Authorization", "Bearer YOUR_KEY_HERE"); http.get(url, { headers: h }).subscribe((x: any) => { var random = Math.floor(Math.random() * x.results.length); this.selectedMovie = x.results[random]; }); } }
The constructor will retrieve the first page of the popular movies from The MovieDB and then get a random movie. Replace YOUR_KEY_HERE with your API key retrieved from The MovieDB.
And last but not least; some styling:
.container { display: flex; flex-direction: row; .poster { padding-right: 10px; img { width: 200px; } } .content{ width: 300px; } }
If you run the application in a web browser you will see something like this:
![Movie rotation example - How To Embed Custom Components With Angular Elements - Kens Learning Curve](https://kenslearningcurve.com/wp-content/uploads/2024/09/Movie-rotation-example-Angular-Elements-Embed-Angular-Components-Kens-Learning-Curve.png)
Awesome! Time to configure it in a way to embed just the movie-rotation component inside a different website.
Using Angular Elements
By default, Angular applications start as an… Application. Thus, we need to make sure it loads a component rather than the whole application. This will need some tweaking in the angular.json, and main.ts. Oh, and adding some package(s).
Install Angular Elements
The first stop is the main.ts, which is configured to bootstrap the application and set the root component AppComponent. But we want to define a custom element, not an application. For this, we need some functionality from Angular Elements. Install the package:
npm install @angular/elements
This could lead to some errors if you are working on an older version:
npm error code ERESOLVE
npm error ERESOLVE unable to resolve dependency tree
npm error
npm error While resolving: movie-rotator@0.0.0
npm error Found: @angular/core@17.3.12
npm error node_modules/@angular/core
npm error @angular/core@”^17.3.0″ from the root project
npm error
npm error Could not resolve dependency:
npm error peer @angular/core@”18.0.1″ from @angular/elements@18.0.1
npm error node_modules/@angular/elements
npm error @angular/elements@”*” from the root project
npm error
npm error Fix the upstream dependency conflict, or retry
npm error this command with –force or –legacy-peer-deps
npm error to accept an incorrect (and potentially broken) dependency resolution.
But no worries; This is easy to fix. As you can see, Angular needs newer versions of @angular/core. If you get these errors, update the packages to the newer versions. Not sure about the versions? Check my package.json on GitHub.
Create the application
Next, open the main.ts and change the code to this:
import { createApplication } from '@angular/platform-browser'; import { createCustomElement } from '@angular/elements'; import { MovieRotationComponent } from './movie-rotation/movie-rotation.component'; createApplication({ providers: [], }) .then((app) => { const movieRotationComponent = createCustomElement(MovieRotationComponent, { injector: app.injector }); customElements.define('movie-rotation', movieRotationComponent); }) .catch((err) => console.error(err));
This will create the application and create a custom element, which can be called in HTML with the tag <movie-rotation></movie-rotation>. Note that you first create the component MovieRotationComponent and then define it.
Angular.json
Let’s configure how our build and other stuff will behave. Since we don’t want to build a complete application but rather an element. This means we don’t need everything that Angular usually gives us. Open the angular.json and look for “builder” under architect -> build. By default, we get the @angular-devkit/build-angular:application builder which is fine, but I rather use the ngx-build-plus.
Ngx-build-plus does everything the normal builder does but with a little bit extra. It extends the default behavior with partial configuration, provides custom functions for building, could build to a single bundle (coming back on this later), and much more. Let’s install this package:
npm i ngx-build-plus
Now, change the “builder” and “options” to the following:
"builder": "ngx-build-plus:browser", "options": { "outputPath": "dist/movie-rotator", "main": "src/main.ts", "index": "src/index.html", "browser": "src/main.ts", "polyfills": [ "zone.js" ], "tsConfig": "tsconfig.app.json", "inlineStyleLanguage": "scss" },
Go a bit lower and find the “budgets” under configurations -> production. Change the settings under the budgets to this:
"index": "", "stylePreprocessorOptions": { "includePaths": [ "src/styles.scss" ] }, "extractLicenses": false, "singleBundle": true
Trying it out
Time to see if the element is actually working. Open the index.html inside the src-folder and change it to this:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>MovieRotator</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> </head> <body> <movie-rotation></movie-rotation> </body> </html>
Run the application as you are used to doing (ng server) and see if it is working. It doesn’t look any different from the previous view, but that is fine. We know we are using the element now and not the component from Angular.
Creating The Element
Let’s build the element and place it on a different website. However, we do this a bit differently than we are used to. Sure, we can use ng build, but we are building an element that we want to (re)use in a different website. We need to build for production and set the optimization off. That last one is important. If something is wrong without the element:
ng build --configuration=production --optimization=false
Executing this command could result in the following error:
Error: bundle initial exceeded maximum budget. Budget 1.05 MB was not met by 1.40 MB with a total of 2.45 MB.
This is because we set the optimization off (false) and thus the files will be bigger. It’s not a big deal. When we are done with creating the element we can set the optimization on and the size will be reduced drastically. To fix this, go to the angular.json and find configurations -> production -> budgets. Inside, change the maximumWarning and maximumError to 3MB. The current size is now 2.45 MB, so 3 MB would be enough.
"budgets": [ { "type": "initial", "maximumWarning": "3MB", "maximumError": "3MB" }, { "type": "anyComponentStyle", "maximumWarning": "2kb", "maximumError": "4kb" } ],
Build it again and it will build the main.js and polyfills.js just fine.
Using The Element
Time to make it work! Go to the dist\movie-rotator folder and copy the main.js and polyfills.js to the directory of the other website. Create an index.html and use the following HTML inside the body:
<h1>Welcome to my website!</h1> <p> Welkom to my website! </p> <movie-rotation></movie-rotation> <script src="main.js"></script> <script src="polyfills.js"></script>
And that’s it! Run the index.html on your server. Note that it doesn’t work if you open the file in your browser. The JavaScript files will not be found due to security reasons. You have to run it inside a server like Wampserver64 for example.
But if you run the website, you will get an error in the code:
NG0908: In this configuration Angular requires Zone.js
Nothing serious, just something we have to adjust in the main.ts of our Angular project. Open it and add the following to the imports:
import 'zone.js';
Rebuild it again for production without the optimization, and copy the main.js and polyfills.js to the directory of your website. Refresh the page:
![Angular Element inside website - How To Embed Custom Components With Angular Elements - Kens Learning Curve](https://kenslearningcurve.com/wp-content/uploads/2024/09/Angular-Element-inside-website-Angular-Elements-Embed-Angular-Components-Kens-Learning-Curve.png)
Clean Up And Optimize
Now that we know the element is working let’s optimize the code and clean up a bit.
Cleaning Up
As you might have noticed we don’t need all the files in the Angular project. We can remove a lot of them. We can delete the complete app folder! None of the files are used a this point. Be sure to rebuild after you do this to make sure nothing breaks.
If you don’t use the assets folder, you can remove this one too.
Rebuild for production to make sure no errors arise. When done, you will see that only the main.js and index.html ios left.
No Index.html
When you build the Angular element you will see the main.js and index.html in the dist folder. Since we don’t need the index.html we can remove it from the building too. This is pretty simple; Just remove it from the angular.json.
Under architect -> build -> builder -> options is an “index”. Remove this line. A bit further, under configurations, you will find another “index”. Don’t remove it, but set the value to “”. It does need an index somewhere.
Optimization
We ‘need’ to do that to keep the JavaScript files small in size. Optimizing does a few things:
- Minification of scripts and styles
- Tree-shaking
- Dead-code elimination
- Inlining of critical CSS
- Fonts inlining
Source: Angular.dev
To activate the optimization, simply replace false with true of the optimization option:
ng build --configuration=production --optimization=false
Before optimization, the main.js had a size of 2.33 MB on my machine. After optimization only 163 KB! That’s a huge difference. This will not only decrease the size but also increase the loading time on your website. The same goes for the polyfills.js.
Copy the main.js and the polyfills.js, since nothing else is left, to the website and try it out.
Conclusion On Angular Elements
Angular Elements is pretty strong when you need some extra functionality on a website where you can’t add it. For example: WordPress. Sure, you can do a lot with JavaScript and jQuery inside WordPress, but I find it way easier to embed Angular Elements into the WordPress page.
Creating custom Angular elements isn’t that hard to do. It does require some configuration and packages. And when you are unlucky, also a lot of updating packages. But when it’s all configured and done, it’s pretty easy to maintain and expand the custom Angular element.
This is just the very basics of Angular Elements. It is also possible to attach events and/or send data from and to the element. It is also possible to use Firestore, for example. But this is for a future article.