Angular 2 animating height-propery -Timing on binding - css

I'd like to have a component, I called it information-board, to animate away after some seconds. I had the idea to make it slide up as exit-animation.
My approach for this is quite simple, since I can do this with css-animations regarding to this thread: Angular 2 Slide Up and Down Animation
My scss-file is looking quite simple:
.info-board {
transition: height 1s;
overflow: hidden;
}
The problem arises in the sentence from the mentioned thread: The height set to 'auto' can't be animated. This makes sense, I guess, but causing me a heap of trouble, because my information-board should be a shared component and also be allowed to be invisible at the start, only being shown when something interesting happens.
So, looking at my component-html:
<div *ngIf="informationMessage">
<div #contentElement class="info-board" [style.height]="contentHeight">
<alert type="{{alertType}}">
<strong>{{ informationHeading }}</strong> {{ informationMessage.messageText }}
</alert>
</div>
</div>
Nothing too fancy: If the informationMessage, a simple dto, is set, it should be shown. I set the height via binding in order to animate it properly.
Now the complicated part: If the info-board is not shown at the beginning, I basically need to wait for the dto to be set AND the html to be rendered. The only hook I found so far is the "afterViewChecked" one, leading to this Code:
#ViewChild('contentElement') public contentElement: ElementRef;
ngAfterViewChecked(): void {
this.checkSetBoardSize();
}
private checkSetBoardSize() {
if (this.contentElement && this.contentElement.nativeElement) {
this.contentHeight = this.contentElement.nativeElement.scrollHeight + 'px';
}
}
This event is the only one I can be sure, that the ViewChild is set, but it seems to be too late: Since this is the last step of the changetracking-logic, the height I set is ignored.
The only other hook, which looked helpful was "ngAfterContentChecked", but in this one, the ViewChild is not set.
I also didn't find a possibility to 'retrigger' the changetracking nor did the approach on Angular 2 #ViewChild in *ngIf with ViewChildren work for me.
Am I missing some sort of possibility? I know, that working with the nativeElements isn't a good idea, but from my research, this is currently needed working with heights.

You could use the animation feature of Angular, the example for ur case can be found in the angular docs
Sidenote: Be aware that the usage of the animation package (it's outsourced to #angular/animations) is changing in angular version >4.0.0-rc.1. For more the the changelog of angular

Related

I'm wondering about custom attributes, CSS attribute selector & repaint

So a little disclaimer: I am completely and utterly self taught. Bear over with me if I'm being a clown.
Anyways, I am currently working on a some platform and in need for a dropdown functionality. That's simple right? Just use HTML5 select tag. However option tags can't be styled :>
So onwards to build my own. The HTML5 select tag uses keyboard input (up/down/enter) for those with disabilities, and I thought I would implement that too. That did present a problem though: The :hover selector collided with my custom attribute, which I use to style keyboard selected items (&[data-selected=true] to be precise).
So onwards to implement my own :hover. And this is where my bewilderment starts.
const handleChildMouseOver = () => {
const items = Array.from(listItem.current?.children!); // The wonders of typescript XD
for (const item of items) {
if (item === event.target) {
item.setAttribute("data-selected", "true");
} else {
item.removeAttribute("data-selected"); // I'm removing the attribute, rather than toggling it, because I got components with 3 states: On, off, and default.
}
}
}
(...)
<ul css={css.list} /*emotion prop*/ data-toggled={toggled} /*parent state*/ onMouseOver={handleChildMouseOver}>
{children} // parent prop
</ul>
So it works as intended, which is fine. But I recall from my pre-react days that you should never manipulate the DOM in loops, as it causes repaints on every iteration. However when I look at the Dev Tools performance profiler, I barely see any "Paints", 8 or so, even when I'm switching hover targets like a madman. What I do see is one million "Composite layer". Oh, and as a bonus React doesn't re-render. Which is fine right? 'Cause I'm not really changing the state of anything, just adding some CSS.
So my question boils down to: Am I being bonkers or smart?
N.B.: I would love to share the actual component, but seeing as this is my first post on stackoverflow, I've got no clue how you do those fancy script tag. Well github is involved somehow, I know that much 🤔

Remove and add class dynamically triggering change detection in between (Angular 2+)

In short, I am trying to trigger a change detection loop between renderer.removeStyle and renderer.addStyle.
The style I am adding is an css animation. When it's removed and added in the same change detection loop Angular won't detect that something changed (for the same animation name).
More Details
Let's say I have a button event (click)="addAnimation('animation1')" that should add existing animation and add the new animation named animation1, animation2 ....
Of course the following code won't work:
addAnimation(animationName: string): void {
this.renderer.removeStyle(this.animate.nativeElement, 'animation');
// setTimeout(() => {
this.renderer.setStyle(this.animate.nativeElement, 'animation', animationName)
// }, 0);
}
since removing and adding a style under angular nose won't trigger any change.
One possible solution is adding a timeout (like the commented code above, but it has side effects that I am not interested in, and also the code is a little wired.
I was hoping to solve it by adding something like this.appRef.tick(); in between to force angular create another change detection loop.
That doesn't work, what am I missing? any suggestions how to do that correctly?
Thanks!
Try following way.
isClick=false;
onClick(){
isClick=true;
}
<div [ngClass]="{'yourCSSClass': isClick}">

Ng-repeat removal and Ng-style modification are not simultaneous

I am trying to implement custom animations on a ng-repeat list. When an element is removed, all the elements that are below it go up.
This is done using a CSS animation on the transform attribute. On the beginning the element is not really removed from the ng-repeat list (there is an animation on the opacity attribute).
At one point, I should actually delete the element from the list. And, at the same time, I need to wind back the animation on the other elements, that have been artificially put too high.
Here is the HTML:
<div ng-repeat="card in cards" ng-style="shouldBeUpped ? uppedStyle : ''">...</div>
And here is the JS:
$timeout(function() {
$scope.shouldBeUpped = false;
$scope.cards.splice(index, 1);
}, 1000);
The problem is that $scope.cards.splice(index, 1); and $scope.shouldBeUpped = false; are not simultaneous. There is a small noticeable delay (maybe 20 or 30 milliseconds), that looks very bad, because in the meantime there is a blank space on the screen ($scope.shouldBeUpped = false; is rendered before $scope.cards.splice(index, 1);).
Do you know what I can do please?
I heard about ng-leave and ng-move classes, but the examples I found on the web don't work for me... (I am using Angular 1.4).
I think you problem is $timeout. You're adding 1s delay there.
I'm not sure how view could know about $scope.shouldBeUpped = false; earlier than $scope.cards.splice(index, 1); as all the changes are reflected when $scope.$apply()

Angular UI bootstrap progressbar minimum width

I am trying to set the minimum width of the angular UI bootstrap progressbar. I checked the docs and they do not mention how to do this. I know for the 'regular' bootstrap you can use something like style="min-width: 10em;". However this only works if you wrap it in the standard progress bootstrap divs like so:
<div class="progress">
<uib-progressbar class="progress-striped active progress-bar" value="value" style="min-width: 10em;">
<span> text </span></uib-progressbar>
</div>
But this displays a progressbar bar without the 'active' animation since regular bootstrap does not support this. When I try it like so it does not set the min-width property
<uib-progressbar class="progress-striped active progress-bar"value="value" style="min-width: 10em;">
<span> text </span>
</uib-progressbar>
edit: I overlooked the animation section in the 'regular' bootstrap docs. I would however like to use the UI bootstrap progressbar if possible.
Regular Bootstrap supports animated progress bars.
Are you sure that you correctly imported Boostrap files? I think you might have included only the CSS file but not the JS. Take a look at the basic template to see which files you should include.
Take also a look at the uib-progressbar documentation. The code snippet you wrote seems to be correct. As I said, I think the reason for this problem is that you didn't include the JS file for Bootstrap.
EDIT: Oh, ui-bootstrap apparently doesn't need Bootstrap's JS, you're right.
Regarding the min-width part of your question: I noticed that you added the progress-bar class to the <uib-progressbar> element. According to the documentation, the progress-bar class should not be used (it will be added by ui-bootstrap to the <div> element that will be rendered inside <uib-progressbar>, and you can easily verify this by inspecting the progress bar width devtools).
Thus, the min-width property is to be applied to the internal <div>. However, since the rendering is managed by angular, the only way to change it is to add a CSS rule like this:
.setminwidth .progress-bar {
min-width: 20em;
}
And then add the new setminwidth class to the external <uib-element> like this:
<uib-progressbar class="progress-striped setminwidth" value="22" type="warning">22%</uib-progressbar>
I tested this but it doesn't seem to work. I think it's because min-width: 0; is hardcoded in the template, and it gets reset everytime ui-bootstrap re-renders the element.
I tried adding !important to the CSS rule, to avoid being overridden, but it doesn't work either.
I guess at this point you should consider why you need to add this min-width property, since ui-bootstrap likes to override it. Could it be because you don't want the progress bar to be "too empty" when the % is low? If that's the case, I think you should look up the changes recently introduced by Bootstrap: it seems that now they add a special min-width for 0%, 1% and 2%.
UPD: The Bootstrap folks apparently changed their mind and reverted the special min-width value. At this point, I think that ui-bootstrap should follow along and remove the hardcoded min-width: 0; as it's not needed anymore. I just sent a pull-request to them. If they merge it, you will be able to use the CSS I posted above.

React on DOM changes caused by Data Binding in Polymer

while exploring the pure awesomeness of Polymer and Dart I struggle to get some things animated right after they get inserted to the DOM by Data Binding.
Example:
<template repeat="{{item in items}}">
<my-item item="{{item}}"></my-item>
</template>
Everytime a new item is inserted or an old one is removed I would like to animate these changes.
Currently I am doing something like this, which works but isn't really nice:
<style>
[item] { transition: 300ms ease-in-out; transform: translateX(0); ]
[require-start-animation] { transform: translateX(-100%); }
</style>
<template repeat="{{item in items}}">
<my-item item="{{item}}" require-start-animation></my-item>
</template>
and before removing it, I add 'require-end-animation' to the elements which will be removed.
The solution works but isn't really that great since I have to keep track of a lot of stuff such as adding the correct attributes at the correct time and removing it. React to transition ending and so on.
Thus I am asking you, is there a cleaner way to react on changes to the DOM which are caused by Data Binding?
You could create a mixin which overrides attached and detached and does the attribute setting/removal before forwarding the call to super (the element itself) and then apply this mixin on elements you want to animate.
I haven't used mixins a lot myself yet. If mixins don't work well for this you could create an implementation in a normal class and then forward the calls to attached and detached to this implementation.
It could also work to create a wrapper element that just wraps the element you want to animate and implements the animation on attach/detach.

Resources