How to properly unload and kill a web component? - javascript

im having a simple web component running on my page. By a button click i simply remove the element expecting that the element and the class behind it is getting killed. But actually it keeps running, for example event listeners are still running even after removing it from the DOM.
This is how i add it to the DOM and make it load:
import { LightningElement, createElement } from 'lwc';
import App from 'my/app';
export default class App extends LightningElement {
...
const appinner = createElement('my-app', { is: App });
document.body.appendChild(app);
...
}
Then i simply remove it by this:
const app = document.querySelector('my-app');
app.remove(); // or app.parentNode.removeChild(app);
Everything in my "App" class is still running even when its gone. How can i really make it unload (or deconstruct) or even kill so no logic keeps running.
Update: Missed to mention i am using LWC library from lwc.dev. And the proper way of injecting an element is described here: Link
Update2: Added more code to show how its really done using the LWC.dev library

Related

Define Web Component inside IFrame

To enhance my tinyMCE Editor, I would like to add some web components and use them inside the editor. Something similar has been done by others, but with older versions of the editor and javascript, which is not valid anymore.
I am aware, that I have to register the tag inside the editor configuration so this works already and my tag makes it to the dom. The problem is, that given components are not defined inside the editors IFrame.
I was not able to add them to the iframes window, the error message doesn't really tell why though. For Testing purposes, I also added the web component to the regular window, where it works flawlessly.
The IFrame:
<iframe srcdoc="<demo-cmp></demo-cmp>" onload="registerComponent(this)"></iframe>
<demo-cmp></demo-cmp>
The Web Component Definition:
class DemoComponent extends HTMLElement {
connectedCallback() {
alert(window.location.href);
}
}
window.customElements.define('demo-cmp', DemoComponent);
function registerComponent(iframe) {
// Exception thrown here
iframe.contentWindow.customElements.define('demo-cmp', DemoComponent);
}
The Error:
Uncaught TypeError: Custom element constructor returned a wrong element
The Fiddle:
https://jsfiddle.net/ynpks9m3/10/

Navigating back causes change detection to break in Angular elements

I am using Angular elements to use my components inside of custom ThingsBoard widgets. The elements are created inside ngDoBootstrap() like this:
const newCustomElement = createCustomElement(CustomElementComponent, {
injector: this.injector,
});
customElements.define("new-custom-element", newCustomElement);
In a ThingsBoard widget I can then import the compiled JavaScript code of my elements via the "Ressources" tab and instantiate them in the onInit() function of the widget like this:
self.onInit = function() {
element = document.createElement(
"new-custom-element");
self.ctx.$container.append(element);
}
While this works just fine and enables me to use Angular components in my widgets, I noticed a strange problem which sometimes occurs when navigating from a ThingsBoard dashboard state back to another dashboard state. Sometimes after navigating back, my widget would seem to load just fine but then the entire change detection seems to be broken. As long as I do not manually call detectChanges() in my component, the component will sometimes appear to freeze entirely in the UI and no longer react to any user interaction (such as a click event). Only refreshing the page will fix this problem.
What could cause this problem and is there anything I can do in order to fix this?
Have you ever tried like this:
ngAfterViewChecked() {
this.cd.detectChanges();
}
or
constructor(private appRef: ApplicationRef) { }
ngAfterViewInit() {
this.appRef.bootstrap(CustomElementComponent);
}
Good luck.

Switching between Vue Router pages cause multiple event fire on VueJS mounted()

I am working on an Electron project where I use Vue CLI project and the Vue CLI Plugin Electron Builder. Everything works great except a weird bug that I found recently.
Whenever I navigate between pages (Vue Router), the event I listen for from the component mounted() property becomes double. It's actually the N+1 issue.
to describe the issue more clearly, I have two Home.vue and HelloWorld.vue components. From Home.vue component, I am sending an event to the main process whenever clicking a button and listening the event.reply() from the same component mounted() property. It's completely as expected at this stage.
However, whenever I go to the HelloWorld page and switch back to the Home page again and when I click the button to send and receive the event from the main process, I don't only see a single event even though I click one time only but I see two event reply. If I switch between pages again, I'll see three event reply and so on like N+1 issue.
For your convenience, I made a quick GIF that will show the issue clearly.
Home.vue
<template>
<div class="home">
<button #click="send()">Home</button>
</div>
</template>
<script>
export default {
name: "Home",
data() {
return {
cause: null
}
},
mounted() {
window.ipcRenderer.on("home:reply", event => console.log(event));
},
methods: {
send() {
window.ipcRenderer.send("home");
}
},
};
</script>
main.js
ipcMain.on("home", event => {
return event.reply("home:reply");
});
I don't have anything special on the Vue Router and it's just default scaffolding that comes with the Vue CLI. As you can see in the above code snippet, all I am doing is just sending an event when clicking a button and listening for the same event reply from the same component mounted() property.
I also found a similar topic on Stack Overflow but couldn't figure it out myself. I have no clue what's wrong on my code 🥱
You need to unregister the event handler when the component is destroyed, otherwise you'll just keep registering the same event handler again and again each time the component is mounted.
mounted() {
window.ipcRenderer.on('home:reply', this.handleHomeReply)
},
destroyed() {
window.ipcRenderer.off('home:reply', this.handleHomeReply)
},
methods: {
handleHomeReply(event) {
console.log(event)
}
}

How to connect raw html <a href> tag to React javascript?

I building out a small web app that simulates browsing WikiPedia by pulling raw html WikiPedia article content (via it's api). I'm then displaying the html in my app by using "dangerouslySetInnerHTML".
I trying to figure out how to allow a user to click on a an unmounted < ahref="wiki/javascript" > tag and capture that event on the React side. I wanted to capture that tag click to update UI state and make server requests.
When using "dangerouslySetInnerHTML" click events are not recognized by react. So I added a function on the global window object on the React side to be clicked on by the tags but get a warning about 'unmounted components' and 'memory leaks'.
How can I prevent the raw unmounted tags from reloading the page and capture that click event in order to update my React's UI?
I think there might be a better way to do what you're trying to do, but with your current implementation:
You can grab a ref on the node that you're doing dangerouslySetInnerHTML, add a click event listener, use event bubbling to capture that link event and finally invoke preventDefault().
class App extends Component {
componentDidMount(){
if(this.myRef){
this.myRef.addEventListener('click', this.anchorLinkListener);
}
}
componentWillUnmount(){
if(this.myRef){
this.myRef.removeEventListener('click', this.anchorLinkListener)
}
}
anchorLinkListener = (e) => {
if(e.target.tagName === 'A') {
console.log('Anchor link clicked')
e.preventDefault();
}
}
render(){
return (
<div
ref={(ref) => {this.myRef = ref}}
dangerouslySetInnerHTML={{__html: 'click me'}}
/>
)
}
}
So I am kind of looking at the problem from another angle.
I am thinking "hmm, dangerouslySetInnerHTML sounds rather ominous, as if I probably shouldn't use it. I wonder if there are nicer, safer alternatives...".
After doing a quick google search for "alternative to dangerouslySetInnerHTML" one of the promising results is this Medium article.
TLDR; we can do cool things with packages like HTML React Parser and that may solve our problem of not being able to capture events properly.
If we have access to the event object once again, we should be able to read its attributes and do whatever else we need to do

Vue.js Router: Run code when component is ready

I'm working on a single-page app with Vue.js and its official router.
I have a menu and a component (.vue file) per every section which I load using the router. In every component I have some code similar to this:
<template>
<div> <!-- MY DOM --> </div>
</template>
<script>
export default {
data () {},
methods: {},
route: {
activate() {},
},
ready: function(){}
}
</script>
I want to execute a piece of code (init a jQuery plugin) once a component has finished transitioning in. If I add my code in the ready event, it gets fired only the first time the component is loaded. If I add my code in the route.activate it runs every time, which is good, but the DOM is not loaded yet, so is not possible to init my jQuery plugin.
How can I run my code every time a component has finished transitioning in and its DOM is ready?
As you are using Vue.js Router, it means that each time you will transition to a new route, Vue.js will need to update the DOM. And by default, Vue.js performs DOM updates asynchronously.
In order to wait until Vue.js has finished updating the DOM, you can use Vue.nextTick(callback). The callback will be called after the DOM has been updated.
In your case, you can try:
route: {
activate() {
this.$nextTick(function () {
// => 'DOM loaded and ready'
})
}
}
For further information:
https://vuejs.org/api/#Vue-nextTick
https://vuejs.org/guide/reactivity.html#Async-Update-Queue
You can use
mounted(){
// jquery code
}
Though this may be a bit late... If I guess correctly, the component having problem stays on the page when you navigate between routes. This way, Vue reuses the component, changing what's inside it that needs to change, rather than destroy and recreate it. Vue provides a key attribute to Properly trigger lifecycle hooks of a component. By changing a component's key, we can indicate Vue to rerender it. See key in guide for details and key in api for code sample.

Categories

Resources