Angular 5 animations enter (void => state) works but leave (state => void) not working












0















Working in Angular 5. Trying to create a carousel type image browser from scratch. User can click/swipe to view next or previous image.



The :enter type animations are working perfectly (shown in my code as "void => next" and "void => prev"). However, the "next => void" and "prev => void" animation transitions are not happening.



Every answer on the web seems to revolve around child components, making style for the element display: block, and calling detectChanges() after changing state. This isn't a child component scenario, I've already assigned "display: block" css to the elements even going so far as to include it in the animation style, and I've made sure to add detectChanges() right after the state change. None of this has worked.



I read on a comment somewhere that detectChanges() doesn't do the trick for :leaving animations anymore. Their workaround was to wrap the code that removes the element from the DOM in a setTimeout() callback. Even this did not work.



It got to the point where I simply copy/pasted the entire code block from https://github.com/born2net/Angular-kitchen-sink/blob/master/src/comps/app2/notes/AnimateCards.ts only changing the variable names. But even that didn't work!



This is making me lose hair. Please help me.



Component (Angular 5 in typescript)



import { Component, OnInit, ChangeDetectorRef, ElementRef, ViewChild } from '@angular/core';
import { trigger, transition, style, animate, keyframes } from '@angular/animations';

type Orientation = ('prev' | 'next' | 'none');

@Component({
selector: 'app-album-browser',
templateUrl: './album-browser.component.html',
styleUrls: ['./album-browser.component.scss'],
animations: [
trigger('animateCarousel',
[
transition('prev => void', // ------> leaving ------->
[
animate('500ms ease-in-out', keyframes([
style({ opacity: 1.0, left: 0 }),
style({ opacity: 0.0, left: 200 })
]))
]),
transition('void => prev', // ----> entering ---->
[
animate('500ms ease-in-out', keyframes([
style({ opacity: 0.0, left: -200, zIndex: 2 }),
style({ opacity: 1.0, left: 0, zIndex: 2 })
]))
]),
transition('next => void', // <------- leaving <-------
[
animate('500ms ease-in-out', keyframes([
style({ opacity: 1.0, right: 0 }),
style({ opacity: 0.0, right: 200 })
]))
]),
transition('void => next', // <------- entering <--------
[
animate('500ms ease-in-out', keyframes([
style({ opacity: 0.0, right: -200, zIndex: 2 }),
style({ opacity: 1.0, right: 0, zIndex: 2 })
]))
])
])
]
})

export class AlbumBrowserComponent implements OnInit, AfterViewInit {

private readonly SWIPE_ACTION = { LEFT: 'swipeleft', RIGHT: 'swiperight'};
public readonly LEFT = 'LEFT';
public readonly RIGHT = 'RIGHT';

public orientation: Orientation = 'none';

public images: string = ;
public selectedImage: string = ;
public changeDetectorRef: ChangeDetectorRef;

constructor(changeDetectorRef: ChangeDetectorRef) {
this.changeDetectorRef = changeDetectorRef;
this.images.push('../../../assets/images/1.jpg');
this.images.push('../../../assets/images/2.jpg');
this.images.push('../../../assets/images/3.jpg');
this.selectedImage.push(this.images[0]);
}

ngOnInit() {
}

public click(direction: string) {
if (direction === this.LEFT) {
this.swipe(this.SWIPE_ACTION.LEFT);
}

if (direction === this.RIGHT) {
this.swipe(this.SWIPE_ACTION.RIGHT);
}
}

public swipe(action = this.SWIPE_ACTION.RIGHT) {

let res: string;
const index = this.images.indexOf(this.selectedImage[0]);

if (action === this.SWIPE_ACTION.LEFT) {
this.orientation = 'next';
this.selectedImage = ;
this.changeDetectorRef.detectChanges();

res = !!this.images[index + 1] ?
this.images[index + 1] :
this.images[0];
}

if (action === this.SWIPE_ACTION.RIGHT) {
this.orientation = 'prev';
this.selectedImage = ;
this.changeDetectorRef.detectChanges();

res = !!this.images[index - 1] ?
this.images[index - 1] :
this.images[this.images.length - 1];
}

this.selectedImage.push(res);
}

}


Template



<div class="album-browser-container">
<div class="left arrow small-glow" (click)="click(LEFT)"></div>
<div class="viewport-frame glow">
<div class="viewport">
<div class="image-slider"
(swipeleft)="swipe($event.type)"
(swiperight)="swipe($event.type)">
<div class="carousel"
*ngFor="let image of selectedImage">
<div class="image-container"
[@animateCarousel]="orientation">
<img [src]="image" class="album-image">
</div>
</div>
</div>
</div>
</div>
<div class="right arrow small-glow" (click)="click(RIGHT)"></div>
</div>









share|improve this question





























    0















    Working in Angular 5. Trying to create a carousel type image browser from scratch. User can click/swipe to view next or previous image.



    The :enter type animations are working perfectly (shown in my code as "void => next" and "void => prev"). However, the "next => void" and "prev => void" animation transitions are not happening.



    Every answer on the web seems to revolve around child components, making style for the element display: block, and calling detectChanges() after changing state. This isn't a child component scenario, I've already assigned "display: block" css to the elements even going so far as to include it in the animation style, and I've made sure to add detectChanges() right after the state change. None of this has worked.



    I read on a comment somewhere that detectChanges() doesn't do the trick for :leaving animations anymore. Their workaround was to wrap the code that removes the element from the DOM in a setTimeout() callback. Even this did not work.



    It got to the point where I simply copy/pasted the entire code block from https://github.com/born2net/Angular-kitchen-sink/blob/master/src/comps/app2/notes/AnimateCards.ts only changing the variable names. But even that didn't work!



    This is making me lose hair. Please help me.



    Component (Angular 5 in typescript)



    import { Component, OnInit, ChangeDetectorRef, ElementRef, ViewChild } from '@angular/core';
    import { trigger, transition, style, animate, keyframes } from '@angular/animations';

    type Orientation = ('prev' | 'next' | 'none');

    @Component({
    selector: 'app-album-browser',
    templateUrl: './album-browser.component.html',
    styleUrls: ['./album-browser.component.scss'],
    animations: [
    trigger('animateCarousel',
    [
    transition('prev => void', // ------> leaving ------->
    [
    animate('500ms ease-in-out', keyframes([
    style({ opacity: 1.0, left: 0 }),
    style({ opacity: 0.0, left: 200 })
    ]))
    ]),
    transition('void => prev', // ----> entering ---->
    [
    animate('500ms ease-in-out', keyframes([
    style({ opacity: 0.0, left: -200, zIndex: 2 }),
    style({ opacity: 1.0, left: 0, zIndex: 2 })
    ]))
    ]),
    transition('next => void', // <------- leaving <-------
    [
    animate('500ms ease-in-out', keyframes([
    style({ opacity: 1.0, right: 0 }),
    style({ opacity: 0.0, right: 200 })
    ]))
    ]),
    transition('void => next', // <------- entering <--------
    [
    animate('500ms ease-in-out', keyframes([
    style({ opacity: 0.0, right: -200, zIndex: 2 }),
    style({ opacity: 1.0, right: 0, zIndex: 2 })
    ]))
    ])
    ])
    ]
    })

    export class AlbumBrowserComponent implements OnInit, AfterViewInit {

    private readonly SWIPE_ACTION = { LEFT: 'swipeleft', RIGHT: 'swiperight'};
    public readonly LEFT = 'LEFT';
    public readonly RIGHT = 'RIGHT';

    public orientation: Orientation = 'none';

    public images: string = ;
    public selectedImage: string = ;
    public changeDetectorRef: ChangeDetectorRef;

    constructor(changeDetectorRef: ChangeDetectorRef) {
    this.changeDetectorRef = changeDetectorRef;
    this.images.push('../../../assets/images/1.jpg');
    this.images.push('../../../assets/images/2.jpg');
    this.images.push('../../../assets/images/3.jpg');
    this.selectedImage.push(this.images[0]);
    }

    ngOnInit() {
    }

    public click(direction: string) {
    if (direction === this.LEFT) {
    this.swipe(this.SWIPE_ACTION.LEFT);
    }

    if (direction === this.RIGHT) {
    this.swipe(this.SWIPE_ACTION.RIGHT);
    }
    }

    public swipe(action = this.SWIPE_ACTION.RIGHT) {

    let res: string;
    const index = this.images.indexOf(this.selectedImage[0]);

    if (action === this.SWIPE_ACTION.LEFT) {
    this.orientation = 'next';
    this.selectedImage = ;
    this.changeDetectorRef.detectChanges();

    res = !!this.images[index + 1] ?
    this.images[index + 1] :
    this.images[0];
    }

    if (action === this.SWIPE_ACTION.RIGHT) {
    this.orientation = 'prev';
    this.selectedImage = ;
    this.changeDetectorRef.detectChanges();

    res = !!this.images[index - 1] ?
    this.images[index - 1] :
    this.images[this.images.length - 1];
    }

    this.selectedImage.push(res);
    }

    }


    Template



    <div class="album-browser-container">
    <div class="left arrow small-glow" (click)="click(LEFT)"></div>
    <div class="viewport-frame glow">
    <div class="viewport">
    <div class="image-slider"
    (swipeleft)="swipe($event.type)"
    (swiperight)="swipe($event.type)">
    <div class="carousel"
    *ngFor="let image of selectedImage">
    <div class="image-container"
    [@animateCarousel]="orientation">
    <img [src]="image" class="album-image">
    </div>
    </div>
    </div>
    </div>
    </div>
    <div class="right arrow small-glow" (click)="click(RIGHT)"></div>
    </div>









    share|improve this question



























      0












      0








      0








      Working in Angular 5. Trying to create a carousel type image browser from scratch. User can click/swipe to view next or previous image.



      The :enter type animations are working perfectly (shown in my code as "void => next" and "void => prev"). However, the "next => void" and "prev => void" animation transitions are not happening.



      Every answer on the web seems to revolve around child components, making style for the element display: block, and calling detectChanges() after changing state. This isn't a child component scenario, I've already assigned "display: block" css to the elements even going so far as to include it in the animation style, and I've made sure to add detectChanges() right after the state change. None of this has worked.



      I read on a comment somewhere that detectChanges() doesn't do the trick for :leaving animations anymore. Their workaround was to wrap the code that removes the element from the DOM in a setTimeout() callback. Even this did not work.



      It got to the point where I simply copy/pasted the entire code block from https://github.com/born2net/Angular-kitchen-sink/blob/master/src/comps/app2/notes/AnimateCards.ts only changing the variable names. But even that didn't work!



      This is making me lose hair. Please help me.



      Component (Angular 5 in typescript)



      import { Component, OnInit, ChangeDetectorRef, ElementRef, ViewChild } from '@angular/core';
      import { trigger, transition, style, animate, keyframes } from '@angular/animations';

      type Orientation = ('prev' | 'next' | 'none');

      @Component({
      selector: 'app-album-browser',
      templateUrl: './album-browser.component.html',
      styleUrls: ['./album-browser.component.scss'],
      animations: [
      trigger('animateCarousel',
      [
      transition('prev => void', // ------> leaving ------->
      [
      animate('500ms ease-in-out', keyframes([
      style({ opacity: 1.0, left: 0 }),
      style({ opacity: 0.0, left: 200 })
      ]))
      ]),
      transition('void => prev', // ----> entering ---->
      [
      animate('500ms ease-in-out', keyframes([
      style({ opacity: 0.0, left: -200, zIndex: 2 }),
      style({ opacity: 1.0, left: 0, zIndex: 2 })
      ]))
      ]),
      transition('next => void', // <------- leaving <-------
      [
      animate('500ms ease-in-out', keyframes([
      style({ opacity: 1.0, right: 0 }),
      style({ opacity: 0.0, right: 200 })
      ]))
      ]),
      transition('void => next', // <------- entering <--------
      [
      animate('500ms ease-in-out', keyframes([
      style({ opacity: 0.0, right: -200, zIndex: 2 }),
      style({ opacity: 1.0, right: 0, zIndex: 2 })
      ]))
      ])
      ])
      ]
      })

      export class AlbumBrowserComponent implements OnInit, AfterViewInit {

      private readonly SWIPE_ACTION = { LEFT: 'swipeleft', RIGHT: 'swiperight'};
      public readonly LEFT = 'LEFT';
      public readonly RIGHT = 'RIGHT';

      public orientation: Orientation = 'none';

      public images: string = ;
      public selectedImage: string = ;
      public changeDetectorRef: ChangeDetectorRef;

      constructor(changeDetectorRef: ChangeDetectorRef) {
      this.changeDetectorRef = changeDetectorRef;
      this.images.push('../../../assets/images/1.jpg');
      this.images.push('../../../assets/images/2.jpg');
      this.images.push('../../../assets/images/3.jpg');
      this.selectedImage.push(this.images[0]);
      }

      ngOnInit() {
      }

      public click(direction: string) {
      if (direction === this.LEFT) {
      this.swipe(this.SWIPE_ACTION.LEFT);
      }

      if (direction === this.RIGHT) {
      this.swipe(this.SWIPE_ACTION.RIGHT);
      }
      }

      public swipe(action = this.SWIPE_ACTION.RIGHT) {

      let res: string;
      const index = this.images.indexOf(this.selectedImage[0]);

      if (action === this.SWIPE_ACTION.LEFT) {
      this.orientation = 'next';
      this.selectedImage = ;
      this.changeDetectorRef.detectChanges();

      res = !!this.images[index + 1] ?
      this.images[index + 1] :
      this.images[0];
      }

      if (action === this.SWIPE_ACTION.RIGHT) {
      this.orientation = 'prev';
      this.selectedImage = ;
      this.changeDetectorRef.detectChanges();

      res = !!this.images[index - 1] ?
      this.images[index - 1] :
      this.images[this.images.length - 1];
      }

      this.selectedImage.push(res);
      }

      }


      Template



      <div class="album-browser-container">
      <div class="left arrow small-glow" (click)="click(LEFT)"></div>
      <div class="viewport-frame glow">
      <div class="viewport">
      <div class="image-slider"
      (swipeleft)="swipe($event.type)"
      (swiperight)="swipe($event.type)">
      <div class="carousel"
      *ngFor="let image of selectedImage">
      <div class="image-container"
      [@animateCarousel]="orientation">
      <img [src]="image" class="album-image">
      </div>
      </div>
      </div>
      </div>
      </div>
      <div class="right arrow small-glow" (click)="click(RIGHT)"></div>
      </div>









      share|improve this question
















      Working in Angular 5. Trying to create a carousel type image browser from scratch. User can click/swipe to view next or previous image.



      The :enter type animations are working perfectly (shown in my code as "void => next" and "void => prev"). However, the "next => void" and "prev => void" animation transitions are not happening.



      Every answer on the web seems to revolve around child components, making style for the element display: block, and calling detectChanges() after changing state. This isn't a child component scenario, I've already assigned "display: block" css to the elements even going so far as to include it in the animation style, and I've made sure to add detectChanges() right after the state change. None of this has worked.



      I read on a comment somewhere that detectChanges() doesn't do the trick for :leaving animations anymore. Their workaround was to wrap the code that removes the element from the DOM in a setTimeout() callback. Even this did not work.



      It got to the point where I simply copy/pasted the entire code block from https://github.com/born2net/Angular-kitchen-sink/blob/master/src/comps/app2/notes/AnimateCards.ts only changing the variable names. But even that didn't work!



      This is making me lose hair. Please help me.



      Component (Angular 5 in typescript)



      import { Component, OnInit, ChangeDetectorRef, ElementRef, ViewChild } from '@angular/core';
      import { trigger, transition, style, animate, keyframes } from '@angular/animations';

      type Orientation = ('prev' | 'next' | 'none');

      @Component({
      selector: 'app-album-browser',
      templateUrl: './album-browser.component.html',
      styleUrls: ['./album-browser.component.scss'],
      animations: [
      trigger('animateCarousel',
      [
      transition('prev => void', // ------> leaving ------->
      [
      animate('500ms ease-in-out', keyframes([
      style({ opacity: 1.0, left: 0 }),
      style({ opacity: 0.0, left: 200 })
      ]))
      ]),
      transition('void => prev', // ----> entering ---->
      [
      animate('500ms ease-in-out', keyframes([
      style({ opacity: 0.0, left: -200, zIndex: 2 }),
      style({ opacity: 1.0, left: 0, zIndex: 2 })
      ]))
      ]),
      transition('next => void', // <------- leaving <-------
      [
      animate('500ms ease-in-out', keyframes([
      style({ opacity: 1.0, right: 0 }),
      style({ opacity: 0.0, right: 200 })
      ]))
      ]),
      transition('void => next', // <------- entering <--------
      [
      animate('500ms ease-in-out', keyframes([
      style({ opacity: 0.0, right: -200, zIndex: 2 }),
      style({ opacity: 1.0, right: 0, zIndex: 2 })
      ]))
      ])
      ])
      ]
      })

      export class AlbumBrowserComponent implements OnInit, AfterViewInit {

      private readonly SWIPE_ACTION = { LEFT: 'swipeleft', RIGHT: 'swiperight'};
      public readonly LEFT = 'LEFT';
      public readonly RIGHT = 'RIGHT';

      public orientation: Orientation = 'none';

      public images: string = ;
      public selectedImage: string = ;
      public changeDetectorRef: ChangeDetectorRef;

      constructor(changeDetectorRef: ChangeDetectorRef) {
      this.changeDetectorRef = changeDetectorRef;
      this.images.push('../../../assets/images/1.jpg');
      this.images.push('../../../assets/images/2.jpg');
      this.images.push('../../../assets/images/3.jpg');
      this.selectedImage.push(this.images[0]);
      }

      ngOnInit() {
      }

      public click(direction: string) {
      if (direction === this.LEFT) {
      this.swipe(this.SWIPE_ACTION.LEFT);
      }

      if (direction === this.RIGHT) {
      this.swipe(this.SWIPE_ACTION.RIGHT);
      }
      }

      public swipe(action = this.SWIPE_ACTION.RIGHT) {

      let res: string;
      const index = this.images.indexOf(this.selectedImage[0]);

      if (action === this.SWIPE_ACTION.LEFT) {
      this.orientation = 'next';
      this.selectedImage = ;
      this.changeDetectorRef.detectChanges();

      res = !!this.images[index + 1] ?
      this.images[index + 1] :
      this.images[0];
      }

      if (action === this.SWIPE_ACTION.RIGHT) {
      this.orientation = 'prev';
      this.selectedImage = ;
      this.changeDetectorRef.detectChanges();

      res = !!this.images[index - 1] ?
      this.images[index - 1] :
      this.images[this.images.length - 1];
      }

      this.selectedImage.push(res);
      }

      }


      Template



      <div class="album-browser-container">
      <div class="left arrow small-glow" (click)="click(LEFT)"></div>
      <div class="viewport-frame glow">
      <div class="viewport">
      <div class="image-slider"
      (swipeleft)="swipe($event.type)"
      (swiperight)="swipe($event.type)">
      <div class="carousel"
      *ngFor="let image of selectedImage">
      <div class="image-container"
      [@animateCarousel]="orientation">
      <img [src]="image" class="album-image">
      </div>
      </div>
      </div>
      </div>
      </div>
      <div class="right arrow small-glow" (click)="click(RIGHT)"></div>
      </div>






      css angular






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 24 '18 at 3:10







      Christian-Capo

















      asked Nov 24 '18 at 0:14









      Christian-CapoChristian-Capo

      11




      11
























          1 Answer
          1






          active

          oldest

          votes


















          0














          TLDR; use *ngIf instead of *ngFor and wrap the updating of the selectedImage in a setTimeout callback.



          I came across the solution while debugging an alternative I came up with. As I debugged, I saw the leave animation working when I put a breakpoint on the array reset line. The GitHub entry I posted above (https://github.com/born2net/Angular-kitchen-sink/blob/master/src/comps/app2/notes/AnimateCards.ts) got me most of the way there, it appears that later releases of Angular created the need to handle the animation in a slightly different way.



          Instead of using an *ngFor directive in the "image-container" div, I'm using an ngIf bound to a class member called "showIt" on the component. I update the orientation and detectChanges() first. Then update the "showIt" to FALSE, followed by another call detectChanges(). This may seem redundant but when I leave out the incremental calls to detectChanges(), it almost seems like there are DOM changes left in a "queue" (for lack of a better term) which end up getting executed on subsequent calls to detectChanges(). Then, I store the target index for the images array in a local variable and finally wrap the subsequent calls to update the selectedImage in a setTimeout callback with the timeout set to the same timing as the transition animation.



          If you're thinking "ugh how inelegant to have to wrap code in a setTimeout callback", I couldn't agree more. But it just seems to be the only way to ensure both :enter and :leave animations are able to transpire. The root cause of my original problem was that when the selectedImage array was set to empty, the DOM was getting updated so quickly that the animation was getting overridden before the leave animation could even be detected by the human eye.



          Final template



          <div class="album-browser-container">
          <div class="left arrow small-glow" (click)="click(LEFT)"></div>
          <div class="viewport-frame glow">
          <div class="viewport">
          <div class="image-slider"
          (swipeleft)="swipe($event.type)"
          (swiperight)="swipe($event.type)">
          <div class="carousel">
          <div class="image-container"
          *ngIf="showIt"
          [@animateCarousel]="orientation">
          <img [src]="selectedImage[0]"
          class="album-image"
          (swipeleft)="swipe($event.type)"
          (swiperight)="swipe($event.type)">
          </div>
          </div>
          </div>
          </div>
          </div>
          <div class="right arrow small-glow" (click)="click(RIGHT)"></div>
          </div>


          The animations from my OP were actually accurate. Only the click / swipe method changed.



          Component (truncated)



          public swipe(action = this.SWIPE_ACTION.RIGHT) {

          let res: string;

          if (action === this.SWIPE_ACTION.LEFT) {
          this.orientation = 'next';
          this.changeDetectorRef.detectChanges();
          this.showIt = false;
          this.changeDetectorRef.detectChanges();

          const index = this.images.indexOf(this.selectedImage[0]);

          res = !!this.images[index + 1] ?
          this.images[index + 1] :
          this.images[0];

          setTimeout(() => {
          this.selectedImage = ;
          this.selectedImage.push(res);
          this.showIt = true;
          this.changeDetectorRef.detectChanges();
          }, 300);

          }

          if (action === this.SWIPE_ACTION.RIGHT) {
          this.orientation = 'prev';
          this.changeDetectorRef.detectChanges();
          this.showIt = false;
          this.changeDetectorRef.detectChanges();

          const index = this.images.indexOf(this.selectedImage[0]);

          res = !!this.images[index - 1] ?
          this.images[index - 1] :
          this.images[this.images.length - 1];

          setTimeout(() => {
          this.selectedImage = ;
          this.selectedImage.push(res);
          this.showIt = true;
          this.changeDetectorRef.detectChanges();
          }, 300);
          }
          }





          share|improve this answer

























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53454123%2fangular-5-animations-enter-void-state-works-but-leave-state-void-not-w%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            0














            TLDR; use *ngIf instead of *ngFor and wrap the updating of the selectedImage in a setTimeout callback.



            I came across the solution while debugging an alternative I came up with. As I debugged, I saw the leave animation working when I put a breakpoint on the array reset line. The GitHub entry I posted above (https://github.com/born2net/Angular-kitchen-sink/blob/master/src/comps/app2/notes/AnimateCards.ts) got me most of the way there, it appears that later releases of Angular created the need to handle the animation in a slightly different way.



            Instead of using an *ngFor directive in the "image-container" div, I'm using an ngIf bound to a class member called "showIt" on the component. I update the orientation and detectChanges() first. Then update the "showIt" to FALSE, followed by another call detectChanges(). This may seem redundant but when I leave out the incremental calls to detectChanges(), it almost seems like there are DOM changes left in a "queue" (for lack of a better term) which end up getting executed on subsequent calls to detectChanges(). Then, I store the target index for the images array in a local variable and finally wrap the subsequent calls to update the selectedImage in a setTimeout callback with the timeout set to the same timing as the transition animation.



            If you're thinking "ugh how inelegant to have to wrap code in a setTimeout callback", I couldn't agree more. But it just seems to be the only way to ensure both :enter and :leave animations are able to transpire. The root cause of my original problem was that when the selectedImage array was set to empty, the DOM was getting updated so quickly that the animation was getting overridden before the leave animation could even be detected by the human eye.



            Final template



            <div class="album-browser-container">
            <div class="left arrow small-glow" (click)="click(LEFT)"></div>
            <div class="viewport-frame glow">
            <div class="viewport">
            <div class="image-slider"
            (swipeleft)="swipe($event.type)"
            (swiperight)="swipe($event.type)">
            <div class="carousel">
            <div class="image-container"
            *ngIf="showIt"
            [@animateCarousel]="orientation">
            <img [src]="selectedImage[0]"
            class="album-image"
            (swipeleft)="swipe($event.type)"
            (swiperight)="swipe($event.type)">
            </div>
            </div>
            </div>
            </div>
            </div>
            <div class="right arrow small-glow" (click)="click(RIGHT)"></div>
            </div>


            The animations from my OP were actually accurate. Only the click / swipe method changed.



            Component (truncated)



            public swipe(action = this.SWIPE_ACTION.RIGHT) {

            let res: string;

            if (action === this.SWIPE_ACTION.LEFT) {
            this.orientation = 'next';
            this.changeDetectorRef.detectChanges();
            this.showIt = false;
            this.changeDetectorRef.detectChanges();

            const index = this.images.indexOf(this.selectedImage[0]);

            res = !!this.images[index + 1] ?
            this.images[index + 1] :
            this.images[0];

            setTimeout(() => {
            this.selectedImage = ;
            this.selectedImage.push(res);
            this.showIt = true;
            this.changeDetectorRef.detectChanges();
            }, 300);

            }

            if (action === this.SWIPE_ACTION.RIGHT) {
            this.orientation = 'prev';
            this.changeDetectorRef.detectChanges();
            this.showIt = false;
            this.changeDetectorRef.detectChanges();

            const index = this.images.indexOf(this.selectedImage[0]);

            res = !!this.images[index - 1] ?
            this.images[index - 1] :
            this.images[this.images.length - 1];

            setTimeout(() => {
            this.selectedImage = ;
            this.selectedImage.push(res);
            this.showIt = true;
            this.changeDetectorRef.detectChanges();
            }, 300);
            }
            }





            share|improve this answer






























              0














              TLDR; use *ngIf instead of *ngFor and wrap the updating of the selectedImage in a setTimeout callback.



              I came across the solution while debugging an alternative I came up with. As I debugged, I saw the leave animation working when I put a breakpoint on the array reset line. The GitHub entry I posted above (https://github.com/born2net/Angular-kitchen-sink/blob/master/src/comps/app2/notes/AnimateCards.ts) got me most of the way there, it appears that later releases of Angular created the need to handle the animation in a slightly different way.



              Instead of using an *ngFor directive in the "image-container" div, I'm using an ngIf bound to a class member called "showIt" on the component. I update the orientation and detectChanges() first. Then update the "showIt" to FALSE, followed by another call detectChanges(). This may seem redundant but when I leave out the incremental calls to detectChanges(), it almost seems like there are DOM changes left in a "queue" (for lack of a better term) which end up getting executed on subsequent calls to detectChanges(). Then, I store the target index for the images array in a local variable and finally wrap the subsequent calls to update the selectedImage in a setTimeout callback with the timeout set to the same timing as the transition animation.



              If you're thinking "ugh how inelegant to have to wrap code in a setTimeout callback", I couldn't agree more. But it just seems to be the only way to ensure both :enter and :leave animations are able to transpire. The root cause of my original problem was that when the selectedImage array was set to empty, the DOM was getting updated so quickly that the animation was getting overridden before the leave animation could even be detected by the human eye.



              Final template



              <div class="album-browser-container">
              <div class="left arrow small-glow" (click)="click(LEFT)"></div>
              <div class="viewport-frame glow">
              <div class="viewport">
              <div class="image-slider"
              (swipeleft)="swipe($event.type)"
              (swiperight)="swipe($event.type)">
              <div class="carousel">
              <div class="image-container"
              *ngIf="showIt"
              [@animateCarousel]="orientation">
              <img [src]="selectedImage[0]"
              class="album-image"
              (swipeleft)="swipe($event.type)"
              (swiperight)="swipe($event.type)">
              </div>
              </div>
              </div>
              </div>
              </div>
              <div class="right arrow small-glow" (click)="click(RIGHT)"></div>
              </div>


              The animations from my OP were actually accurate. Only the click / swipe method changed.



              Component (truncated)



              public swipe(action = this.SWIPE_ACTION.RIGHT) {

              let res: string;

              if (action === this.SWIPE_ACTION.LEFT) {
              this.orientation = 'next';
              this.changeDetectorRef.detectChanges();
              this.showIt = false;
              this.changeDetectorRef.detectChanges();

              const index = this.images.indexOf(this.selectedImage[0]);

              res = !!this.images[index + 1] ?
              this.images[index + 1] :
              this.images[0];

              setTimeout(() => {
              this.selectedImage = ;
              this.selectedImage.push(res);
              this.showIt = true;
              this.changeDetectorRef.detectChanges();
              }, 300);

              }

              if (action === this.SWIPE_ACTION.RIGHT) {
              this.orientation = 'prev';
              this.changeDetectorRef.detectChanges();
              this.showIt = false;
              this.changeDetectorRef.detectChanges();

              const index = this.images.indexOf(this.selectedImage[0]);

              res = !!this.images[index - 1] ?
              this.images[index - 1] :
              this.images[this.images.length - 1];

              setTimeout(() => {
              this.selectedImage = ;
              this.selectedImage.push(res);
              this.showIt = true;
              this.changeDetectorRef.detectChanges();
              }, 300);
              }
              }





              share|improve this answer




























                0












                0








                0







                TLDR; use *ngIf instead of *ngFor and wrap the updating of the selectedImage in a setTimeout callback.



                I came across the solution while debugging an alternative I came up with. As I debugged, I saw the leave animation working when I put a breakpoint on the array reset line. The GitHub entry I posted above (https://github.com/born2net/Angular-kitchen-sink/blob/master/src/comps/app2/notes/AnimateCards.ts) got me most of the way there, it appears that later releases of Angular created the need to handle the animation in a slightly different way.



                Instead of using an *ngFor directive in the "image-container" div, I'm using an ngIf bound to a class member called "showIt" on the component. I update the orientation and detectChanges() first. Then update the "showIt" to FALSE, followed by another call detectChanges(). This may seem redundant but when I leave out the incremental calls to detectChanges(), it almost seems like there are DOM changes left in a "queue" (for lack of a better term) which end up getting executed on subsequent calls to detectChanges(). Then, I store the target index for the images array in a local variable and finally wrap the subsequent calls to update the selectedImage in a setTimeout callback with the timeout set to the same timing as the transition animation.



                If you're thinking "ugh how inelegant to have to wrap code in a setTimeout callback", I couldn't agree more. But it just seems to be the only way to ensure both :enter and :leave animations are able to transpire. The root cause of my original problem was that when the selectedImage array was set to empty, the DOM was getting updated so quickly that the animation was getting overridden before the leave animation could even be detected by the human eye.



                Final template



                <div class="album-browser-container">
                <div class="left arrow small-glow" (click)="click(LEFT)"></div>
                <div class="viewport-frame glow">
                <div class="viewport">
                <div class="image-slider"
                (swipeleft)="swipe($event.type)"
                (swiperight)="swipe($event.type)">
                <div class="carousel">
                <div class="image-container"
                *ngIf="showIt"
                [@animateCarousel]="orientation">
                <img [src]="selectedImage[0]"
                class="album-image"
                (swipeleft)="swipe($event.type)"
                (swiperight)="swipe($event.type)">
                </div>
                </div>
                </div>
                </div>
                </div>
                <div class="right arrow small-glow" (click)="click(RIGHT)"></div>
                </div>


                The animations from my OP were actually accurate. Only the click / swipe method changed.



                Component (truncated)



                public swipe(action = this.SWIPE_ACTION.RIGHT) {

                let res: string;

                if (action === this.SWIPE_ACTION.LEFT) {
                this.orientation = 'next';
                this.changeDetectorRef.detectChanges();
                this.showIt = false;
                this.changeDetectorRef.detectChanges();

                const index = this.images.indexOf(this.selectedImage[0]);

                res = !!this.images[index + 1] ?
                this.images[index + 1] :
                this.images[0];

                setTimeout(() => {
                this.selectedImage = ;
                this.selectedImage.push(res);
                this.showIt = true;
                this.changeDetectorRef.detectChanges();
                }, 300);

                }

                if (action === this.SWIPE_ACTION.RIGHT) {
                this.orientation = 'prev';
                this.changeDetectorRef.detectChanges();
                this.showIt = false;
                this.changeDetectorRef.detectChanges();

                const index = this.images.indexOf(this.selectedImage[0]);

                res = !!this.images[index - 1] ?
                this.images[index - 1] :
                this.images[this.images.length - 1];

                setTimeout(() => {
                this.selectedImage = ;
                this.selectedImage.push(res);
                this.showIt = true;
                this.changeDetectorRef.detectChanges();
                }, 300);
                }
                }





                share|improve this answer















                TLDR; use *ngIf instead of *ngFor and wrap the updating of the selectedImage in a setTimeout callback.



                I came across the solution while debugging an alternative I came up with. As I debugged, I saw the leave animation working when I put a breakpoint on the array reset line. The GitHub entry I posted above (https://github.com/born2net/Angular-kitchen-sink/blob/master/src/comps/app2/notes/AnimateCards.ts) got me most of the way there, it appears that later releases of Angular created the need to handle the animation in a slightly different way.



                Instead of using an *ngFor directive in the "image-container" div, I'm using an ngIf bound to a class member called "showIt" on the component. I update the orientation and detectChanges() first. Then update the "showIt" to FALSE, followed by another call detectChanges(). This may seem redundant but when I leave out the incremental calls to detectChanges(), it almost seems like there are DOM changes left in a "queue" (for lack of a better term) which end up getting executed on subsequent calls to detectChanges(). Then, I store the target index for the images array in a local variable and finally wrap the subsequent calls to update the selectedImage in a setTimeout callback with the timeout set to the same timing as the transition animation.



                If you're thinking "ugh how inelegant to have to wrap code in a setTimeout callback", I couldn't agree more. But it just seems to be the only way to ensure both :enter and :leave animations are able to transpire. The root cause of my original problem was that when the selectedImage array was set to empty, the DOM was getting updated so quickly that the animation was getting overridden before the leave animation could even be detected by the human eye.



                Final template



                <div class="album-browser-container">
                <div class="left arrow small-glow" (click)="click(LEFT)"></div>
                <div class="viewport-frame glow">
                <div class="viewport">
                <div class="image-slider"
                (swipeleft)="swipe($event.type)"
                (swiperight)="swipe($event.type)">
                <div class="carousel">
                <div class="image-container"
                *ngIf="showIt"
                [@animateCarousel]="orientation">
                <img [src]="selectedImage[0]"
                class="album-image"
                (swipeleft)="swipe($event.type)"
                (swiperight)="swipe($event.type)">
                </div>
                </div>
                </div>
                </div>
                </div>
                <div class="right arrow small-glow" (click)="click(RIGHT)"></div>
                </div>


                The animations from my OP were actually accurate. Only the click / swipe method changed.



                Component (truncated)



                public swipe(action = this.SWIPE_ACTION.RIGHT) {

                let res: string;

                if (action === this.SWIPE_ACTION.LEFT) {
                this.orientation = 'next';
                this.changeDetectorRef.detectChanges();
                this.showIt = false;
                this.changeDetectorRef.detectChanges();

                const index = this.images.indexOf(this.selectedImage[0]);

                res = !!this.images[index + 1] ?
                this.images[index + 1] :
                this.images[0];

                setTimeout(() => {
                this.selectedImage = ;
                this.selectedImage.push(res);
                this.showIt = true;
                this.changeDetectorRef.detectChanges();
                }, 300);

                }

                if (action === this.SWIPE_ACTION.RIGHT) {
                this.orientation = 'prev';
                this.changeDetectorRef.detectChanges();
                this.showIt = false;
                this.changeDetectorRef.detectChanges();

                const index = this.images.indexOf(this.selectedImage[0]);

                res = !!this.images[index - 1] ?
                this.images[index - 1] :
                this.images[this.images.length - 1];

                setTimeout(() => {
                this.selectedImage = ;
                this.selectedImage.push(res);
                this.showIt = true;
                this.changeDetectorRef.detectChanges();
                }, 300);
                }
                }






                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Nov 24 '18 at 6:58

























                answered Nov 24 '18 at 6:33









                Christian-CapoChristian-Capo

                11




                11






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53454123%2fangular-5-animations-enter-void-state-works-but-leave-state-void-not-w%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    Berounka

                    Sphinx de Gizeh

                    Different font size/position of beamer's navigation symbols template's content depending on regular/plain...