What is CodePen?
CodePen is a popular online code editor and community platform that allows developers to write and share code snippets. It offers a convenient way to showcase interactive elements and test code functionalities.
Step-by-Step Guide: Integrate CodePen into Your Angular Project
Integrating CodePen snippets into your Angular project involves this main steps:
Embed the HTML Code:
- Open your desired CodePen snippet
- Click on "Embed" located at the bottom
- Copy the HTML code provided (Do not include the script tag)
<p class="codepen" data-height="300" data-theme-id="dark" data-default-tab="result" data-slug-hash="NWOQYqz" data-editable="true" data-user="Rico-the-bold" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;"> <span>See the Pen <a href="https://codepen.io/Rico-the-bold/pen/NWOQYqz"> Material Buttons</a> by Rico (<a href="https://codepen.io/Rico-the-bold">@Rico-the-bold</a>) on <a href="https://codepen.io">CodePen</a>.</span> </p>
Integrate a Script Service:
- Create a new service named
ScriptService
to handle script loading dynamically. - This service will ensure the CodePen script is loaded only once and reloaded if needed.
import { DOCUMENT } from "@angular/common"; import { Inject, Injectable, Renderer2 } from "@angular/core"; @Injectable({ providedIn: "root", }) export class ScriptService { constructor(@Inject(DOCUMENT) private document: Document) {} addJsScript(renderer: Renderer2, src: string) { const existingScript = this.document.querySelector(`script[src="${src}"]`); if (existingScript) return; const script = renderer.createElement("script"); script.type = "text/javascript"; script.src = src; renderer.appendChild(this.document.body, script); } reloadJsScript(renderer: Renderer2, src: string) { this.removeJsScript(src); this.addJsScript(renderer, src); } private removeJsScript(src: string): void { const existingScript = this.document.querySelector(`script[src="${src}"]`); if (existingScript) { existingScript.remove(); } } ceckIfJsScriptExist(src: string): boolean { return !!this.document.querySelector(`script[src="${src}"]`); } }
- Create a new service named
Integrate a Consent Service:
So that the user's consent can be obtained in accordance with the GDPR before we load CodePen, we integrate a
ConsentService
.import { isPlatformServer } from "@angular/common"; import { Inject, Injectable, PLATFORM_ID } from "@angular/core"; @Injectable({ providedIn: "root", }) export class ConsentService { constructor(@Inject(PLATFORM_ID) private platformId: Object) {} giveConsent(serviceName: string) { if (isPlatformServer(this.platformId)) return; localStorage.setItem(serviceName + "Consent", "true"); } checkConsent(serviceName: string): boolean { if (isPlatformServer(this.platformId)) return false; return localStorage.getItem(serviceName + "Consent") === "true"; } }
Integrate a CodePen Service:
Now we integrate a
CodepenService
.import { Inject, Injectable, PLATFORM_ID, Renderer2 } from "@angular/core"; import { ConsentService } from "./consent.service"; import { ScriptService } from "./script.service"; import { isPlatformServer } from "@angular/common"; @Injectable({ providedIn: "root", }) export class CodepenService { constructor(private consentS: ConsentService, private scriptS: ScriptService, @Inject(PLATFORM_ID) private platformId: Object) {} loadCodePen(renderer: Renderer2) { if (isPlatformServer(this.platformId)) return; if (this.consentS.checkConsent("CodePen")) { this.scriptS.reloadJsScript(renderer, "https://cpwebassets.codepen.io/assets/embed/ei.js"); } else { const CodePenDemos = Array.from(document.querySelectorAll(".codepen")); for (const demo of CodePenDemos) { const spans = Array.from(demo.querySelectorAll("span")); for (const span of spans) { const button = renderer.createElement("button"); renderer.addClass(button, "codePenConsentButton"); button.addEventListener("click", () => this.consentS.giveConsent("CodePen")); const buttonText = renderer.createText("Load content from CodePen"); renderer.appendChild(button, buttonText); const parent = span.parentNode; renderer.insertBefore(parent, button, span); renderer.removeChild(parent, span); const paragraph = renderer.createElement("p"); const paragraphText = renderer.createText("The code is loaded by the third-party provider CodePen. For more details see "); renderer.appendChild(paragraph, paragraphText); const privacyPolicy = renderer.createElement("a"); renderer.setAttribute(privacyPolicy, "href", "/privacy-policy"); const privacyPolicyText = renderer.createText("Privacy Policy"); renderer.appendChild(privacyPolicy, privacyPolicyText); renderer.appendChild(paragraph, privacyPolicy); renderer.appendChild(parent, paragraph); } } } } }
For a nicer consent button, a bit of scss:
.codepen:has(.codePenConsentButton) { flex-direction: column; } .codePenConsentButton { $color: rgb(193, 192, 255); padding: 1em; background-color: $color; border-radius: 4px; border: 3px solid transparent; cursor: pointer; display: flex; gap: 0.5em; font-size: 1.2em; transition: all 250ms; margin: 0 auto; &:hover { color: rgb(238, 238, 238); border-color: $color; background-color: transparent; } + p { font-size: 0.8em; text-wrap: balance; text-align: center; } }
Load CodePen in your component:
Finally, we load CodePen in the component:
import { AfterViewChecked, Component, OnInit, Renderer2, ViewEncapsulation } from "@angular/core"; import { CodepenService } from "src/app/services/codepen.service"; @Component({ selector: "app-blog-post", templateUrl: "./blog-post.component.html", styleUrls: ["./blog-post.component.css"], }) export class BlogPostComponent implements AfterViewChecked { constructor(private renderer: Renderer2, private codePenS: CodepenService) {} ngAfterViewChecked() { this.codePenS.loadCodePen(this.renderer); } }