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.
Related
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
I'm building a mobile app using ionic 4 and it's two languages ar and en, the menu drawer is a pre-built component.
So I need to refresh the drawer component to get's the right styles based on the dom direction rtl or ltr
What I'm doing now is just location.reload reloading the whole app but I don't think that a good approach for doing that
Simplest way to do is to call ngOnInit something like
fn() {
this.ngOnInit();
}
Or try this
this.router.navigateByUrl('/RefreshComponent', { skipLocationChange: true })
.then(() => {
this.router.navigate(['Your actualComponent']);
});
call this method whenever you want to reload your page .
that will reload the page
window.location.reload();
For Refreshing or re drawing the component use the changeDetectionStratergy
so inject the ChangeDetectionRef Service in the component where you using the build-in component so on any event call the detectChange method for redrawing the component.
I am using the togglable tabs component in Bootstrap. I would like a table in each tab. Each one is displaying a table of email logs (Click Event, Open Event, etc). I would also like each table to be loaded dynamically with Vue-resource only once the user clicks on that tab, and for the resource/component to only be loaded once (once we have the data from AJAX, don't refresh it).
How can I set this up? I currently have an email-table component and an email-table-template template that renders the table, but I'm not sure how to set those up to render themselves when the user clicks the tab, and to only call the AJAX once.
An illustration of the task
Here is my current code for detecting the tab switch and newing up a Vue component:
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
var email_event = $(e.target).data('email-event');
switch(email_event) {
case 'click':
createClick();
break;
// rest of the cases
}
function createClick() {
var click_events = Vue.resource('/api/email_logs/click_events');
click_events.get().then((response) => {
new Vue({
el: '#table-click',
data: {
searchQuery: '',
gridColumns: ['campaign_id', 'target_link_name', 'target_link_url', 'created_at'],
gridData: response.body
}
})
});
Any insight is appreciated. Thanks very much!
If you want to call a method only once you can use listen and emit events.
vm.$once and vm.$emit should do the trick.
Official documentation
https://v2.vuejs.org/v2/api/#vm-once
Here is a quick example
https://jsfiddle.net/leocoder/s1nfsao7/4/
From official documentation
If you want to keep the switched-out components in memory so that you can preserve their state or avoid re-rendering, you can wrap a dynamic component in a <keep-alive> element
Sample:
<keep-alive> <component :is="currentView"> <!-- inactive components will be cached! --> </component> </keep-alive>
In the above example "currentView" is to be set to a component name you want to load/display
Docs: https://v2.vuejs.org/v2/api/#keep-alive
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.
TL;DR: With Angular UI Router, is it possible to autoscroll to a view of the same state that the template displaying it belongs to?
The Setup
I have an Angular 1.4.1 app, using UI Router 0.2.15 and Angular Material 0.10.0.
Example Plunk
There is a parent state called website and three child states: home, foo and bar.
The parent state has a wrapper view, which is displayed via the <ui-view> tag in index.html. It also has two other views, header#website and footer#website, which it displays via the wrapper view template.
Each of the three child states has a main view, which is displayed via the parent state's wrapper view template.
What I Expect
In the parent state's wrapper view template, when I set autoscroll="true" on the ui-view tag for the header#website view, I expect that the page will scroll to the top whenever the state changes. See the accepted answer of this SO question.
What Is Happening
When any of the ui-sref links in the footer is clicked, the page does not scroll to the top.
What I've Tried
If I put autoscroll="true" on the ui-view tag for the main view, it works as expected. This is not what I want, however, because the header is hidden from view even when you navigate to a state from the address bar.
What I Suspect
I think the problem has to do with the fact that header#website is a view that belongs to the website state, and that autoscroll only works for views that normally are displayed on the current template. In other words, normally the header view would be displayed via index.html, not wrapper.html.
The Question
Is my suspicion above correct? Do I need to refactor, or is there a way to make this work as is?
Thanks in advance!
Though I don't advocate DOM manipulation outside of a directive, a quick and dirty solution would be to add a scrollTo method in a .run block of your top level module definition, for ex:
.run(function ($window, $rootScope) {
$rootScope.$on('$viewContentLoaded', function () {
var interval = setInterval(function () {
if (document.readyState == "complete") {
window.scrollTo(0, 0);
clearInterval(interval);
}
}, 1);
});
})
http://plnkr.co/edit/qPqy69o7F9aTjNbUTMGY?p=preview
(borrowed from here https://stackoverflow.com/a/22166553/1516309)
I tried using the $anchorScroll() method of ui-router, but since your links are at the bottom of the page, thats where the page scrolls to, so you dont actually notice it doing anything.