Crear una extensión de Chrome usando Angular
Este tutorial se basa en la versión 3 (MV3) del manifiesto de la extensión de Chrome y también en la versión 2+ de Angular (2, 3 y…). Si no ha usado la versión 3 del manifiesto, puede seguir este tutorial para migrar del manifiesto V2 a V3.
Al final de este artículo, podrá demostrar su aplicación Angular dentro de una ventana emergente de extensión de Chrome o un cuadro de diálogo (mediante el uso de un iframe que se inicia mediante un archivo de secuencia de comandos de contenido de extensión de Chrome).
Configuraciones de aplicaciones angulares
Primero, cree y desarrolle su aplicación Angular deseada como desee. Significa que cuando desarrolle una aplicación Angular no piense en ninguna configuración relacionada con la extensión de Chrome y desarrolle su aplicación como siempre.
Después de terminar el desarrollo de su aplicación Angular, solo necesita 2 cambios para preparar su aplicación Angular para implementarla dentro de la extensión de Chrome.
- Cambie el "href base" de la aplicación dentro de la aplicación Angular index.html.
<base href="/app/index.html">
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Application Title</title>
<!-- <base href="/" /> --> // Remove this
<base href="/app/index.html"> // Use this instead of the above line
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>
<body>
<app-root></app-root>
</body>
</html>
useHash: true
RouterModule.forRoot(routes, { useHash: true })
ng build --prod
El siguiente paso nos mostrará cómo usar la aplicación Angular dentro de la extensión de Chrome.
Configuraciones de extensiones de Chrome
En primer lugar, vale la pena señalar que este tutorial se basa en la versión 3 (MV3) del manifiesto de la extensión de Chrome.
¿Dónde queremos demostrar nuestra aplicación Angular en nuestra extensión de Chrome? ¿ Ventana emergente de extensión de Chrome o un cuadro de diálogo (usando iframe)?
Para una mejor comprensión, vea las imágenes a continuación, la primera está relacionada con la aplicación Angular dentro de la ventana emergente de la extensión de Chrome y la segunda está relacionada con la aplicación Angular dentro del iframe de la extensión de Chrome.
Ahora, vamos a explicar cada uno paso a paso.
Use la ventana emergente de la extensión de Chrome para mostrar la aplicación Angular
- Cree una carpeta en el directorio raíz del proyecto de extensión de Chrome y asígnele el nombre de la aplicación , y luego, copie y pegue los archivos de compilación de la aplicación Angular desde la carpeta dist dentro de ella. Tenga en cuenta que para pegar solo el contenido de la carpeta dist de la aplicación Angular .
Consulte la estructura de directorios del proyecto a continuación.
Sample Extension Project Directory
-- app
- index.html
.
.
.
-- assets
- cancel.png
- icon.png
-- popup
- popup.js
- popup.html
- popup.css
-- background.js
-- content_script.js
-- manifest.json
"manifest_version": 3,
"name": "Angular App",
"version": "1.0.1",
.
.
.
"action": {
"default_icon": "assets/icon.png",
"default_title": "Angular App",
"default_popup": "app/index.html" // Please, look at the path carefully!
},
.
.
.
"web_accessible_resources": [
{
"resources": [
"app/index.html"
.
.
.
]
}
]
En este enfoque, crearemos un iframe dentro del archivo de script de contenido de la extensión de Chrome y luego agregaremos nuestra aplicación Angular a este iframe para ver nuestra aplicación dentro de un cuadro de diálogo (modal) en la pantalla.
Para implementar esto, prefiero crear una función para abrir y cerrar el diálogo (modal) por una identificación única. Entonces, con esta función, puede crear y mostrar tantos diálogos como desee entre sí y cada uno tiene una identificación única.
- Cree la función openDialog() dentro del archivo content_script.js . Esta función obtendrá 3 argumentos:
- id : la identificación única del diálogo.
- opciones : las opciones personalizadas que desea configurar en el cuadro de diálogo.
- contentPath : la ruta de la ruta de la aplicación Angular inicial que quiere mostrarse inmediatamente después de abrir el cuadro de diálogo. Tenga en cuenta que para escribir la ruta de la aplicación Angular inicial después de la ruta del directorio de la aplicación Angular dentro del proyecto de extensión de Chrome. Por ejemplo, si los archivos de compilación de la aplicación Angular (el contenido de la carpeta dist) están en la carpeta de la aplicación dentro del directorio raíz de la extensión de Chrome, entonces el contentPath se verá así: "app/index.html#/" . O, si desea abrir una ruta específica de la aplicación Angular (por ejemplo, /store), entonces la ruta de contenido de "/store" ( la ruta de la ruta Angular que se muestra dentro del cuadro de diálogo) se verá así: "app/index. html#/tienda” .
- id : la identificación única del diálogo que se cerrará.
const preModalContainerId = '_container-';
function openDialog(id, options, contentPath) {
// First load dialog html file
getHtmlContent(contentPath).then(modalHtmlContent => {
// Create dialog overlay (a black layer behind the dialog body)
const overlayElem = document.createElement("div");
overlayElem.classList.add('myext-modal-overlay');
overlayElem.setAttribute("id", id);
// Create an iframe for showing dialog body (in Chrome extension the dialog should be used with iframe)
const iframeElement = document.createElement("iframe");
// Check if options has iframe class name for styling
if (options?.iframe?.className) {
iframeElement.classList.add(options.iframe.className);
} else {
iframeElement.setAttribute("style", "width: 100%; height: 100%;");
}
// Assign src for iframe to show Angular app
iframeElement.src = chrome.runtime.getURL(contentPath);
// If overlay has cancel button then prepare to clear iframe from screen by click on cancel icon
if (options?.overlay?.hasCancelBtn) {
const cancelButton = document.createElement("span");
// if options has cancel button class name for styling
if (options?.overlay?.cancelBtnImage) {
cancelButton.style.backgroundImage = 'url(' + getAssetImgPath(options.overlay.cancelBtnImage) + ')';
} else {
cancelButton.innerText = '+';
}
// if options has cancel button class name for styling
if (options?.overlay?.cancelBtnClassName) {
cancelButton.classList.add(options.overlay.cancelBtnClassName);
} else {
cancelButton.classList.add('myext-modal-overlay-cancel-btn');
}
overlayElem.appendChild(cancelButton);
jq(cancelButton).click(() => {
closeDialog(id);
});
}
// Set iframe as dialog overlay child
overlayElem.appendChild(iframeElement);
// Dialog body container
const modalBodyContainerId = preModalContainerId + id;
const modalBodyContainer = document.createElement("div");
modalBodyContainer.classList.add('myext-modal-container');
modalBodyContainer.setAttribute("id", modalBodyContainerId);
// Add dialog html file content to dialog body container
modalBodyContainer.innerHTML = modalHtmlContent;
document.body.appendChild(overlayElem);
document.body.appendChild(modalBodyContainer);
});
}
function getAssetImgPath(assetPath) {
return chrome.runtime.getURL(assetPath);
}
async function getHtmlContent(htmlFileUrl) {
let fileUrl;
// Check if html file url is decoded or not
if (htmlFileUrl.includes('chrome-extension://')) {
fileUrl = htmlFileUrl;
} else {
fileUrl = chrome.runtime.getURL(htmlFileUrl);
}
return await (await fetch(fileUrl)).text();
}
function closeDialog(id) {
const overlayElem = document.getElementById(id);
const modalBodyContainerId = preModalContainerId + id;
const modalBodyContainer = document.getElementById(modalBodyContainerId);
if (overlayElem) overlayElem.remove();
if (modalBodyContainer) modalBodyContainer.remove();
}
.myext-modal-overlay {
display: inline-block;
position: fixed;
width: 100%;
height: 100%;
left: 0;
top: 0;
background: rgba(51, 51, 51, 0.7);
z-index: 1000000;
}
.myext-modal-overlay-cancel-btn {
position: absolute;
width: 50px;
height: 50px;
left: 50px;
top: 50px;
font-size: 2.5em;
color: white;
background-color: #c57070;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
transform: rotate(45deg);
cursor: pointer;
z-index: 1001;
transition: all 0.25s ease-in-out;
}
.myext-modal-overlay-cancel-btn:hover {
background-color: #b06565;
box-shadow: 0 2px 10px #666;
}
.myext-modal-container {
display: block;
position: fixed;
border-radius: 18px;
overflow: hidden;
box-shadow: 0 2px 50px #444;
background: #1d222f;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 2001;
}
.myext-iframe {
position: fixed;
border-radius: 35px;
overflow: hidden;
box-shadow: 0 2px 50px #444;
top: 50px;
right: 50px;
height: 630px;
width: 390px;
}
.myext-modal-overlay-custom-cancel-btn {
position: absolute;
width: 50px;
height: 50px;
top: 35px;
right: 35px;
border-radius: 50%;
background-repeat: no-repeat;
object-fit: cover;
background-size: cover;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
z-index: 1001;
transition: all 0.25s ease-in-out;
}
.myext-modal-overlay-custom-cancel-btn:hover {
box-shadow: 0 2px 10px #666;
}
"manifest_version": 3,
"name": "Angular App",
"version": "1.0.1",
.
.
.
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [
"content_script.js",
],
"css": [
"style.css"
],
"run_at": "document_end",
"all_frames": false
}
],
"action": {
"default_icon": "assets/icon.png",
"default_title": "Angular App",
"default_popup": "app/index.html" // Please, look at the path carefully!
},
.
.
.
"web_accessible_resources": [
{
"resources": [
"app/index.html"
"style.css",
.
.
.
],
"matches": [
"<all_urls>"
]
}
]
const dialogOptions = {
iframe: {
className: 'myext-iframe'
},
overlay: {
hasCancelBtn: true,
cancelBtnClassName: 'myext-modal-overlay-custom-cancel-btn',
cancelBtnImage: 'assets/images/cancel-btn.png'
}
}
openDialog('example-id', dialogOptions, 'app/index.html#/');
closeDialog('example-id');
Muchas gracias por leer este artículo.

![¿Qué es una lista vinculada, de todos modos? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































