Angular Elements WebComponents external DOM Changes - javascript

TL;DR: Deleting the DOM Element of a custom Element created with Angular Elements will lead to sub-routers not loading components
First, the code is available at Github.
Sadly, I did not get a stackblitz version running, but locally, after a clone, npm install and ng serve it should run fine.
About the general structure:
The Project shows an example of a WebComponent exposed by an Angular App via Elements.
The WebComponent is used in the index.html, which is referred as container Application.
The use case here is a little bit constructed, but the navigation that occurs, when clicking
on the two top bottoms is comparable to the real world use case.
Normally this WebComponent would have been used in an AngularJS legacy Application. Since there are many overlappings between the two root views (overview1 overview2) it is used in two AngularJS components, in between ui-router handled navigation. This means, that exactly this constructed actions happen. The DOM Elements will be deleted and re-added.
So basically the problem is:
If I am deleting DOM Elements of a WebComponent (so deleting the CustomElement itself) from external, and then re-adding it with another route, The Child-route components of this will not get loaded. If there is a short delay (50ms), everything works fine.
To reproduce the problem:
Load page and click an the "switch with delay" button.
Reveal Detail Component
Switch to the other Overview by clicking "switch with delay" again.
Reveal Detail Component
=> Everything should work fine
Repeat the same steps, but this time click only on "switch directly".
Prior Investigations
What I already debugged is the Router. So I went through the log messages with "enable tracing"
and they seemed to be no differences. Afterwards I compared the Components' Lifecycle and
the thing I noticed is, that in the working example the old Detail**1**Component Object will get destroyed a new Detail**1**Component one will be created and directly destroyed afterwards and then everything regarding the Detail**2**Component will get constructed.
On the not working example it is like this:
A new Detail**1**Component will get constructed again and destroyed afterwards. Then the old Detail**1**Component will get destroyed. Then nothing regard the Detail**2**Component will get constructed.
So routing seems to work fine, but components won't get loaded in this case, maybe due to a strange lifecycle, because of the Detachment of the View to the ViewModel happening due to hard deleting those WebComponent in DOM.
Maybe I am just dumb and did something fundamentally wrong, but I could not find anything in the Web regarding this problem and every solution I tried by myself just resulted in a workaround like establishing a delay.

You can find a workaround available on Github.
I forced the recreation of the DOM object by attaching a boolean
to the navigation button, which will determine if the DetailComponent
should actually get shown.
<router-outlet *ngIf="shown">
Then the Outlet will get reevaluated and the DOM will get refilled on clicking the button.
This will effectively mitgating the issues described.
In my opinion its not the cleanest solution, but cleaner than a timeout, that
even needs to get applied from the outside.
I hope this was helpful and maybe someone might point me to the actual fix and
not only workaround.
Since this was my first question do not hesitate to give me feedback on formal
appearance.

Related

No events are generated in Vue

There is a js file that is built using the Laravel Mix. It previously had 2 components, after which there was a need to register a new one. After I registered all the necessary logic, I decided to launch it. Assembled bundle. I check. Does not respond to pressing. Everything is correct in the code. There were no errors during assembly. The developer console is also empty. I decided to just write console.log. You never know. Nothing helped. As a result, I made a copy of current_ source.vue, and in the original I erased everything and wrote simply
<template>
<v-btn #click="console.log('adfesfwf')">ajvarhe</v-btn>
</template>
Yes, yes, I have Vuetify connected. Buttons are correctly displayed, but in the console, by pressing, it is empty. At the same time, I use DevTools for Vue, where it is very convenient to view dispatched events. For all the time debugging this error, not a single event was recorded. At the same time, hooks work (checked only created). I tried to create a completely different component. Exactly the same problem. I want to draw attention to the fact that the previous 2 components work correctly. How many rebuilds were there, events work.
If you want to subscribe to a native event, you must use a special modifier native.
In your case, it will look like this <v-btn #click.native="console.log('adfesfwf')">ajvarhe</v-btn>

Unload reactjs component

I have got a website, that is lazyloading react scripts from different sources. For each script loaded, we provide a div with the name of the script as id. As soon as the script is loaded, it searches for the div with the id and renders the components.
As the site is displayed on a stationary tablet it does not reload very often and the memory footprint gets pretty big. Is there a way to completely unload a react script without reloading the website? Is there even a way to just unload any
kind of script? I guess the garbage collector is responsible for this, but currently its not even removing scripts / components that have unmounted a long time ago.
As I was searching for a solution, I found this thread about angular. I'm basicly looking for a way to do the same with react (Even tho I didn't test the angular solution).
Before removing the script tag and the container DOM node, you can use unmountComponentAtNode to allow React to do its cleanup.
ReactDOM.unmountComponentAtNode(document.getElementById('root'));
Use a design pattern that uses conditional rendering and check in the componentDidMount if either data is returned or the specific section is to be rendered.

Is it possible to have a plugin triggered on any page load without having to re initialize it on every view loaded

I do not have any code to display as code would not help towards my question. I am wondering if there is a advanced JavaScript technique that can allow my plugin be called and ran against all items needed but without having to initialize it on every view in mb. Now the layout shared method is not what I mean. So fir example if I placed said plugin in a global scope in one he file and did a regular
$(document).ready(call plugin unit here)
Would that then be bound to every page and view so I do not need it to be added on every view or is thee some kind of event I can bind to when i initialize my plugin within the global.js file so it will be ran on any view or HTML page that the user goes to working on its elements? Kind of how using the older method event binding ".live" would handle all current items and future ones.
I am trying to find this out or find out if possible how to handle it. My plugin I'm testing with is proprietary so cannot post code here but uses the regular concept of a each loop on on elements affected. Hopefully my question has been stated with a clear goal.
Now understanding that it needs to run against any new target items that may not have been loaded prior but may come down the pipe a few page navigations away I'm not sure I can do this. Unless there is some kind of event in the global JavaScript scope I can bind to and during initialization of my plugin within the one section of code will then cause it to run against all items on what ever page is loaded. Please no rude downing with "we need to see code" as that would not affect an answer here and as mentioned the plugin cannot be publically displayed. Please helpful comments only and sorry if over complicating this and overlooking the answer. Thanks in advance.

MWC: how to rerender on URL change

Heyo everyone,
story time - skip if you don't care
I'm just starting out with Meteor + Polymer using Synthesis by #aruntk and I'm very happy about the results and greatful for the time he's invested in this project. There's one issue I'm having though.
I've previously only changed a iron-pages object to change the content of my view. Putting that in a FlowRouter like FlowRouter.route("/", action: {ironpages.select("home");}); works just fine. However, my site is getting more complex and I want to rerender a whole section now. I'm being told to do it reactively which is (to my poor understanding) the preferred way of building Apps here.
tl;dr - skip to here if you don't care about stories
So what I did is just putting mwcLayout.render("test-layout",{"main":"yas-manual-page"}); in my Router action. However, I have to reload to make the changes visible which is not what I want.
the router action is being called when changing the URL
the mwcLayout.render() call works if I reload the page once in the initial building of the site
calling mwcLayout.render() again at a later point does not do anything
I've read up on the topic and people say it's a problem with single-page apps and not building it reactively and whatnot, but I have no idea how this is not reactive. It's reacting to the URL change.
Please, if you have a minute, share some insight with me, I'm really stuck. :slight_smile:
Have a wonderful day y'all!
disclaimer: it's a repost form the Meteor forums which suggests coming here instead.
This behavior is added as a feature of mwc layout to prevent multiple re rendering during each route change. Workarounds here are to create another mwc layout or to set third argument forceRender. From the mwc:layout docs
forceRender
In mwc:layout we dont re render the layout unless the new layout is not equal to the current layout or forceRender argument is set. This is to prevent unwanted rerendering while changing routes(even if you change a param/queryparam the route gets rerun so does the render function written inside FlowRouter action). forceRender comes in handy when you have to change the rendering while keeping the current layout.
...
<mwc-layout id="demo-landing">
<div region="header"></div>
<div region="main"></div>
</mwc-layout>
...
imports/startup/client/router.js
...
action:function(params,queryParams){
mwcLayout.render("demo-landing",{"main":"test-layout1","header":"test-header"});
}
...
Now if you try
mwcLayout.render("demo-landing",{"main":"test-layout2","header":"test-header"});
from console it wont work since layout is not changed and forceRender is not set.
This works->
mwcLayout.render("demo-landing",{"main":"test-layout","header":"test-header"},true);

Removing Reactjs Om components (ClojureScript)

I am trying to make a tabbed windowing system within a webpage using om-bootstrap's "pills" navigation by adding tabs when links get clicked and removing tabs when an X button on the tabs is clicked.
I need to know how to add and remove data from the global state/store and create a macro that can be used to declare a tab app component and make it remove itself when it is no longer alive.
What is the best way to reference the global state? How can I make a component remove/unmount itself when it gets closed?
Since removal of a subcomponent affects its owner, you should let the owner (i. e. the "tab system") know that this tab needs to be closed/destroyed/obliterated.
I've digged through todomvc example (live) assuming your process of destroying tab panes is pretty much the same as destruction of TODO items there. I see nothing ocnflicting so far. Here are my findings:
A channel is used.... When application starts (IWillMount), a (chan) (from core.async) is written into application state at :comm key.
...for event handling.... Events from the channel are handled in the loop following that code, in go-form, asynchronously with the block it appears in (with <! being a "kinda blocking" operation). Well, you may know it, I didn't, still learning what is CLJS all about.
...that is passed to all child items' init states.... So it becomes a way for children to send events to the root. I'm starting to like this.
...so they can send events to their parent! This is done in put! calls with the comm channel, fetched in the linked line. Events put there are handled by the loop defined in (2), which delegates them to appropriate functions depending on type (accompanying keyword).
I'm nowhere near a ClojureScript pro, but I'm learning. So if the above doesn't make sense, this is normal and means I didn't understand something. If that turns out to be the case, putting me back on track would be much appreciated.

Categories

Resources