I want to add conditions to load this script in the head tag. I need to check if the URL is from our live domain then only it runs otherwise not. I could simply add this script to the head if there was no condition requirement. How I can inject this script in the head tag in angular?
For eg:
window.origin == 'https://www.stackoverflow.com' then inject this script into the head tag otherwise not.
<script>
(function(h,o,t,j,a,r){
h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
h._hjSettings={hjid:*******,hjsv:*};
a=o.getElementsByTagName('head')[0];
r=o.createElement('script');r.async=1;
r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
a.appendChild(r);
})(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');
</script>
You can handle this in one of your components, preferably in app.component. You could check the URL of course but more elegant would be to have some flag like enableHotjarTracking in your environment.ts file and set it to true only for your production environment.
Then you could call it as follows in your app.component.ts
import { environment } from './environments/environment';
import { DOCUMENT } from '#angular/common';
...
// inject document wrapper
constructor(
#Inject(DOCUMENT) private document: Document,
){}
ngAfterContentInit() {
if(environment.enableHotjarTracking) {
((h, o, t, j, a, r) => {
h.hj =
h.hj ||
/* tslint:disable:only-arrow-functions */
function () {
(h.hj.q = h.hj.q || []).push(arguments);
};
h._hjSettings = { hjid: xxxx, hjsv: x };
a = o.getElementsByTagName('head')[0];
r = o.createElement('script');
r.async = 1;
r.src = t + h._hjSettings.hjid + j + h._hjSettings.hjsv;
a.appendChild(r);
})(window as any, this.document, 'https://static.hotjar.com/c/hotjar-', '.js?sv=');
}
}
You could also keep your hotjar id in the environment file.
Related
I have a web-component at root level. The simplified version of which is shown below:
class AppLayout {
constructor() {
super();
this.noShadow = true;
}
connectedCallback() {
super.connectedCallback();
this.render();
this.insertAdjacentHTML("afterbegin", this.navigation);
}
render() {
this.innerHTML = this.template;
}
get template() {
return `
<h1>Hello</h1>
`;
}
navigation = `
<script type="module">
import './components/nav-bar.js'
</script>
`;
}
customElements.define('app-layout', AppLayout);
I want to load a script after this component loads. The script creates html for navigation and tries to add it to the app-layout element shown above. However, even though, it does find the app-layout element, it is unable to append the navBar element. It is, however, able to append the navBar to the body of the html. Any ideas what I'm missing.
const navLinks =
`<ul>
<li>Some</li>
<li>Links</li>
</ul>
`;
const navBar = document.createElement('nav');
navBar.innerHTML = navLinks;
const appLayout = document.querySelector('app-layout'); // works with 'body' but not with 'appLayout'
console.log(appLayout); // Logs correct element
appLayout.appendChild(navBar);
I know that what I'm trying to do here (loading a script inside a web component) is not ideal, however, I would like to still understand why the above doesn't work.
using innerHTML or in your case insertAdjacentHTML to add <script> tags to the document doesn't work because browsers historically try to prevent potential cross site script attacks (https://www.w3.org/TR/2008/WD-html5-20080610/dom.html#innerhtml0)
What you could do is something like:
const s = document.createElement("script");
s.type = "module";
s.innerText = `import './components/nav-bar.js'`;
this.append(s);
// or simply directly without the script: `import('./comp..')` if it is really your only js in the script tag.
I am trying to add tracking script in .js file to my angular 12 application.
(function() {
var ds = document.getElementsByTagName("script")[0];
var dm = document.createElement("img");
dm.width = 1;
dm.height = 1;
dm.alt = " ";
dm.src = "https://example.com/url=" + window.location.href;
ds.parentNode.insertBefore(dm, ds);
})();
Script fires properly on the first page, but doesn't fire on navigating to another page on my website. When i check in elements window.location.href is not changing for the new page. It is because of SPA. How can i get latest location on all pages and script to fire on all pages
You could do it the Angular way, although idk if that will work.
You can go into your root component (should be AppComponent), and simply throw this inside the constructor or ngOnInit()
#Component({
...
})
export class AppComponent {
constructor(router: Router) {
router.events.pipe(
filter(route => route instanceof NavigationEnd),
concatMap((route) => ajax('https://example.com/url=' + route.url)),
).subscribe();
}
}
Unless you need your tracker to be an Img for some reason, or you get permission / CORS issues. This should work nicely
I'm trying to create a React Portal that when mounted, requires running a specific line to load an ActiveReports Designer component.
Here's is my portal code:
constructor(props: IWindowPortalProps) {
super(props);
this.containerEl = document.createElement('div'); // STEP 1: create an empty div
this.containerEl.id = 'designer-host';
this.containerEl.className = styles.designerHost;
this.externalWindow = null;
}
private copyStyles = (sourceDoc: Document, targetDoc: Document) => {
Array.from(sourceDoc.styleSheets).forEach(styleSheet => {
if (styleSheet.cssRules) { // true for inline styles
const newStyleEl = sourceDoc.createElement('style');
Array.from(styleSheet.cssRules).forEach(cssRule => {
newStyleEl.appendChild(sourceDoc.createTextNode(cssRule.cssText));
});
targetDoc.head.appendChild(newStyleEl);
} else if (styleSheet.href) { // true for stylesheets loaded from a URL
const newLinkEl = sourceDoc.createElement('link');
newLinkEl.rel = 'stylesheet';
newLinkEl.href = styleSheet.href;
targetDoc.head.appendChild(newLinkEl);
}
});
}
componentDidMount() {
this.externalWindow = window.open('', '', `height=${window.screen.height},width=${window.screen.width}`);
this.externalWindow.document.body.appendChild(this.containerEl);
this.externalWindow.document.title = 'A React portal window';
this.externalWindow.addEventListener('load', () => {
new Designer('#designer-host');
});
}
render() {
return ReactDOM.createPortal(null, this.containerEl);
}
However, when the new window loads, I get the error
Error: Cannot find the host element. at Function.<anonymous>
which indicates that the designer-host div is not there. I think the load function points to the main DOM and not the new window's one.
Alternatively, I tried appending the ActiveReports .js file by doing in my componentDidMount()
s.type = "text/javascript";
s.src = "../node_modules/#grapecity/activereports/lib/node_modules/#grapecity/ar-js-designer/index.js";
this.externalWindow.document.head.append(s);
and then assigning the Designer instantiation on the onLoad property of the element. Again with no luck.
Is there maybe a way I could run JavaScript a code after the portal has been loaded and point to that DOM?
Thank you
I work for GrapeCity. Could you please go to our support portal and submit a ticket. We will need a full code sample for us to be able to answer this question. Please give us a download link to the sample within the ticket.
Thank you
We are trying to change CSS id's based on time. The point is that currently, it manipulates the body. How can we change it into section manipulation?
Angular part
ngOnInit() {
this.run(1000, 10)
}
run(interval, frames) {
var int = 1;
function func() {
document.body.id = "b"+int;
int++;
if(int === frames) { int = 1; }
}
var swap = window.setInterval(func, interval);
}
HTML
<section class='full-screen'>
...
...
</section>
there are different css snippets for #b1, #b2, #b3... since above code changes these ids during each time period. I assume something should be changed here:
document.body.id = "b"+int;
How move that function usage from body into above HTML section?
Add a Template reference variable in your template for the section tag:
<section #section class='full-screen'>
...
...
</section>
Add a #ViewChild decoratored variable in your component's ts file to get this element:
#ViewChild('section', { read: ElementRef }) mySection: ElementRef;
Now you can use it like this in your component's ts file:
ngOnInit() {
this.run(1000, 10)
}
run(interval, frames) {
var int = 1;
function func() {
this.mySection.nativeElement.id = "b"+int;
int++;
if(int === frames) { int = 1; }
}
var swap = window.setInterval(func.bind(this), interval);
}
See this simple DEMO
UPDATE:
Note that you're using function func(), this will cause you a scoping problem with using this as your component object. One way to fix this is by using bind function:
var swap = window.setInterval(func.bind(this), interval);
Updated the demo to show this in action.
document.getElementById("div_top1").setAttribute("id", "div_top2");
You can use this to change section id.
You can do it thanks to angular viewChild feature
in your html:
<div #foo class="myClass"></div>
in your component ts file
import { Component, ViewChild, ElementRef, AfterViewInit } from '#angular/core';
// ....
export MyComponernt implement AfterViewInit {
// ....
#ViewChild('foo') foo: ElementRef;
// ....
ngAfterViewInit(): void {
// this is how you manipulate element id properly thanks to angular viewChild feature
this.foo.nativeElement.id = 'something';
}
// ....
}
I follow official doc for integrating TinyMCE with Angular: https://www.tinymce.com/docs/integrations/angular2/.
Now everything works fine, but TinyMCEs plugins loaded in application start, but I want it load asynchronously from /src/assets/tinymce. Question is,
How can I load TinyMCE asynchronously when I need it?
~ apologies for grammatical mistakes
In .angular-cli.json I have scripts array:
"scripts": [
"../node_modules/tinymce/tinymce.js",
"../node_modules/tinymce/themes/modern/theme.js",
"../node_modules/tinymce/plugins/link/plugin.js",
"../node_modules/tinymce/plugins/paste/plugin.js",
"../node_modules/tinymce/plugins/table/plugin.js",
"../node_modules/tinymce/plugins/image/plugin.js",
"../node_modules/tinymce/plugins/imagepicker/plugin.js",
"../node_modules/tinymce/plugins/imagetools/plugin.js",
"../node_modules/tinymce/plugins/advlist/plugin.js",
"../node_modules/tinymce/plugins/autolink/plugin.js",
"../node_modules/tinymce/plugins/lists/plugin.js",
"../node_modules/tinymce/plugins/charmap/plugin.js",
"../node_modules/tinymce/plugins/print/plugin.js",
"../node_modules/tinymce/plugins/preview/plugin.js",
"../node_modules/tinymce/plugins/hr/plugin.js",
"../node_modules/tinymce/plugins/anchor/plugin.js",
"../node_modules/tinymce/plugins/pagebreak/plugin.js",
"../node_modules/tinymce/plugins/searchreplace/plugin.js",
"../node_modules/tinymce/plugins/wordcount/plugin.js",
"../node_modules/tinymce/plugins/visualblocks/plugin.js",
"../node_modules/tinymce/plugins/visualchars/plugin.js",
"../node_modules/tinymce/plugins/fullscreen/plugin.js",
"../node_modules/tinymce/plugins/insertdatetime/plugin.js",
"../node_modules/tinymce/plugins/media/plugin.js",
"../node_modules/tinymce/plugins/nonbreaking/plugin.js",
"../node_modules/tinymce/plugins/save/plugin.js",
"../node_modules/tinymce/plugins/contextmenu/plugin.js",
"../node_modules/tinymce/plugins/directionality/plugin.js",
"../node_modules/tinymce/plugins/template/plugin.js",
"../node_modules/tinymce/plugins/textcolor/plugin.js",
"../node_modules/tinymce/plugins/colorpicker/plugin.js",
"../node_modules/tinymce/plugins/textpattern/plugin.js",
"../node_modules/tinymce/plugins/toc/plugin.js",
"../node_modules/tinymce/plugins/code/plugin.js",
"../node_modules/tinymce/plugins/autoresize/plugin.js",
"../node_modules/tinymce/plugins/help/plugin.js"
],
these scripts are plugins of TinyMCE
Declare these 2 on top in your component:
declare var tinymce: any;
const url = '//cdn.tinymce.com/4/tinymce.min.js';
Use this function to load the script in your component's onInit function:
loadScript() {
let scriptTag = document.querySelector('script[src="//cdn.tinymce.com/4/tinymce.min.js"]');
let node;
if (!scriptTag) {
node = document.createElement('script');
node.src = url;
node.type = 'text/javascript';
node.async = true;
node.charset = 'utf-8';
document.getElementsByTagName('head')[0].appendChild(node);
}
}
and then use the following function to load the tinymce:
loadTinyMCE() {
if (tinymce)
tinymce.init({
selector: '#tinymce',
skin_url: 'assets/skins/lightgray',
plugins: ['paste', 'link', 'autoresize','image'],
setup: editor => {
this.editor = editor;
}
})
}
and finally destroy the tinymce object on ngdestroy:
ngOnDestroy() {
tinymce.remove();
}
It might be the bad approach but it saves me from loading the script on application load and gives me the ability to load it only when it is needed.