This is pretty strange. I have an Angular directive that is part of an ng-repeat, and there are no elements in the ng-repeat's source array until a specific action is performed by the user.
Once that action is performed, the array is populated correctly and the element is added to the DOM -- but doesn't actually render. On literally any DOM change -- altering any CSS property of any element on the pages, resizing the page, anything -- the element appears as expected.
Not sure what code would actually be useful to help diagnose this problem, but I'm happy to provide anything relevant.
Other relevant information: Works as expected in Safari, using Ionic framework.
It's not enough that the user perform an action, Angular must find out that the user has done so. You are supposed to inform it by calling $digest() but as that can fail if you are already in a digest call, I just call $timeout().
Related
I've been search for quite a while, but i wasn't able to find a good solution to this.
I have to trigger a function/event, as soon as the DOM of a component finished rendering.
I need to access the height of the component, so ngAfterViewInit won't work, as that is called before the DOM is rendered.
I tried ngAfterViewChecked, but this gets called multiple times. As the function that gets executed is part of a wrapper-app, i have no access to that either, and i absolutely need to make sure it only gets fired once. I was thinking about using ngAfterViewChecked with some sort of debounce, but this feels (and probably is) wrong.
The alternative would be to trigger a function from the template, when the ngFor loop reaches the last item. But again, that just feels wrong.
Any input is appreciated
I got around this issue by using ngAfterViewChecked. On every check, i get the scrollHeight of the components body. Before it renders, it will be 0, so checking if there's been a change on the scrollHeight attribute, works for my use case.
Thanks for all the comments, that actually made me think of this solution
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.
In the below template, I would expect the script tag to never render, and the alert script to never execute. However it does.
<div ng-if="false">
<script>alert('should not run')</script>
Should not appear
</div>
This is causing us huge performance problems on mobile devices as we wrap large DOM and directive structures in ng-ifs with the expectation they will not render when the condition is false.
I have also tested ng-switch which behaves in the same manner.
Is this expected behaviour? Is there a way to avoid the unnecessary render?
JSFiddle
It may seem backward, but ngIf deals more with the removal of DOM, rather than the addition. Before the controller finishes instantiating, the DOM still exists. This is generally a good thing, and allows you to have graceful degradation for users without JS (or, alternatively, an initial loading state).
If you don't want the inner DOM to render, place it in a directive (either its own directive, or via ng-include) or in a view.
Example 1 (understanding why the script runs):
To help yourself understand the flow a bit better, you can update the example to instead be:
<div ng-if="false">
{{"Should not appear"}}
<script>alert('should not run')</script>
</div>
https://jsfiddle.net/hLw0nady/6/embedded/result/
You will notice that when the alert pops up, Angular has not yet interpolated "Should not appear" (it appears in its braces). After you dismiss the alert, however, it disappears.
Example 2 (how to prevent the alert):
An example of hiding the code that "should not run" in a directive can be viewed here: https://jsfiddle.net/hLw0nady/4/
In this example, only if you replace ng-if="false" with ng-if="true" will you get your alert.
I think that false expression is not well converted to angular false.
I can prove this by setting:
<div ng-if="!true">
Which doesn't render text in current div.
Anyway, it executes alert, i suppose it is executed before angular runs, that's why.
Recently I am finding difficulty understanding whats happening in a CoffeeScript/Backbone app. Its hard to trace whats happening quickly without a very slow step through. The problem I think is: I know an event is triggered (Backbone view event). But I dont know which functions are called because of it. There maybe more than 1. I may not even know with view partial is the event defined (so I cant put a breakpoint?)
Is there a debugger which plots the execution of the program as a graph? So that I can zoom into what I need, or maybe something I can use to "visualize" the execution of my code. Maybe not, if what should I be looking out for. I am not sure where I need to put a breakpoint since I dont know where some events are triggered. Then sometimes I find it hard to understand why the code step through might be jumping here and there, maybe its multiple events and their handlers executing?
Everything in Backbone (Views, Models, Collections, Routers) extends Backbone.Events. That means they have an _events property that contains each bound event (e.g. change) with an array of their subscribers.
In order to access this open your javascript console in chrome, firefox or safari (or anything but IE) and enter the name of a globally accessible instantiated object with ._events at the end. E.g.
products._events
After pressing enter you should be able to expand this and see what is published and subscribed.
I'm still running into the same problem, filters and functions inside ng-repeat being called all the damn time.
Example here, http://plnkr.co/edit/G8INkfGZxMgTvPAftJ91?p=preview, anytime you change something on a single row, someFilter filter is called 1000 times.
Apparently it's because any change on a child scope bubbles up to its parent, causing $digest to run, causing all filters to run(https://stackoverflow.com/a/15936362/301596). Is that right? How can I prevent it from happening in my particular case?
How can I make it run only on the item that has changed?
In my actual use case the filter is called even when the change is not even on the items of ng-repeat, it's so pointless and it is actually causing performance problems..
// edit cleared all the unnecessary stuff from the plunker
http://plnkr.co/edit/G8INkfGZxMgTvPAftJ91?p=preview
This is just how Angular's dirty checking works. If you have an array of 500 items and the array changes, the filter must be reapplied to the entire array. And now you're wondering "why twice"?
From another answer:
This is normal, angularjs uses a 'dirty-check' approach, so it need to call all the filters to see if exists any change. After this it detect that have a change on one variable(the one that you typed) and then it execute all filters again to detect if has other changes.
And the answer it references: How does data binding work in AngularJS?
Edit: If you're really noticing sluggishness (which I'm not on an older Core 2 Duo PC), there are probably a number of creative ways you can get around it depending on what your UI is going to be.
You could put the row into edit mode while the user is editing the data to isolate the changes, and sync the model back up when the user gets out of edit mode
You could only update the model onblur instead of onkeypress using a directive, like this: http://jsfiddle.net/langdonx/djtQR/1/