This is awful, and for Angular 2+ but just for the record here's one idea.
I have two button
elements, one for when the user has items in their shopping cart, and one for when they don't.
The easiest way by far is to put position: relative
on the parent DIV and position: absolute
on both the buttons. The main disadvantage is the parent DIV has to be sized manually, and things like centering becoming trickier.
If the intent is to delay adding to the DOM based on an Observable value, then I thought 'Why not just delay the observable value?' which will have the same end effect. This needs to be done only when the transition is from false > true though because you only want to hide it when it is coming into view. So I used a pipe to handle this.
<!-- This button for when item is IN the cart -->
<button [@cartIconAnimation] *ngIf="showCartIcon | delayTrue | async">View Cart</button>
<!-- This button for when item is NOT IN the cart -->
<button [@cartIconAnimation] *ngIf="showCartIcon | complement | delayTrue | async">Add to Cart</button>
This assumes showCartIcon
is Observable<boolean>
.
Then the pipes are as follows, and no delay is required on your animation criteria.
@Pipe({
name: 'delayTrue'
})
export class DelayTruePipe implements PipeTransform {
constructor() {}
transform(value: Observable<any> | any, delay: number): Observable<any> {
if (isObservable(value)) {
return value.pipe(distinctUntilChanged(), debounce((show) => show ? timer(delay || 500) : empty()));
} else {
throw Error('Needs to be an observable');
}
}
}
@Pipe({
name: 'complement'
})
export class ComplementPipe implements PipeTransform {
constructor() {}
transform(value: Observable<boolean> | any): Observable<any> {
if (isObservable(value)) {
return value.pipe(map(i => !i));
} else {
throw Error('Needs to be an observable');
}
}
}
Note: The delay used by the pipe must be greater than the time it takes for the previous item to disappear, or you'll have the same problem.
The complement pipe just inverts the boolean value.
This solution works, but it's hacky and the timing might be harder to get wrong and there may be race conditions as two different browser timers fire off at the same time. I'd only do something like this if you really can't use position: absolute
.