Jump to content
Search Community

Trouble understanding onCompleteScope in an Angular application

Balslev test
Moderator Tag

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

Greetings Greensock community

 

I have a "simple" question, which i cannot for the life of me figure out on my own.

In Angular, i have several methods in my component, it could be "closeEverything()", normally i call such function with "this.closeEverything()".
Now, when i try to call such method in a TweenLite.to onComplete callback, it says undefined function. So far so good. Greensock does not know anything about the Angular scope, and i reckon Angular does now know anything about the Greensock scope.

 

Now from what i have read, i might be able to change the scope Greensock uses in the onCompleteScope property, but i have not been able to make it work. 

Does anyone have an example, where out-of-scope functions are being called in Tween callbacks?  What is it i am missing, i feel like a complete newbie again, trying to implement this otherwise awesome library into my existing Angular application.

 

/Balslev

Link to comment
Share on other sites

I managed to fix it... it does feel wrong, so if anyone have a better/more smooth way to do it, i would love to know.
I apply my angular component to a variable first, and then i use that variable to access all my angular functions.

Is this really the way?

 

update() {
  // here i apply my component to a variable
  let mainComponent:MainComponent = this;

  // mainComponent (aka the whole component) is available for use, and the Angular methods are available
  TweenLite.to(this.target, 0.6, {
   opacity:0, scale:0, svgOrigin:"0 0", onComplete:mainComponent.close()
  });
}

 

Link to comment
Share on other sites

Hi and welcome to the GreenSock forums,

 

It's really tough to guess what is going to work in your application. 

A small demo would really help as explained here: 

Working with callback scope can be tricky until you see it in practice.

In the world of GSAP the scope of a callback is the tween or timeline that is calling the callback.

If a tween calls an onComplete callback, then the keyword "this" inside that function is going to refer to that tween.

If you want "this" to represent some other object, then you need to specify that in the callback scope.

 

Please study this example and see if it helps
 

var myObject = {
  x:1234567
}


TweenLite.to("h1", 1, {x:100, onComplete:tweenScope});
TweenLite.to("h3", 2, {x:100, onComplete:myObjectScope, onCompleteScope:myObject});

function tweenScope(){
  // this = the first tween
  console.log("the duration of this tween is " + this.duration()); //1
}

function myObjectScope() {
  // "this" = myObject
  console.log("the x of myObject = " + this.x) //1234567
}

 

View on codepen and open the console

 

See the Pen pqBeMR?editors=1011 by GreenSock (@GreenSock) on CodePen

 

I suspect in your case you want a scope of mainComponent or this

 

also, I don't think you want the () when defining your callback

 

usually bad because close() will execute immediately

onComplete:mainComponent.close();

 

good

onComplete:mainComponent.close;

 

 

  • Like 3
Link to comment
Share on other sites

1 hour ago, OSUblake said:

A demo would be helpful because your code doesn't look right. It should probably be something like this.

 


update() {

  TweenLite.to(this.target, 0.6, {
   opacity:0, 
    scale:0, 
    svgOrigin:"0 0", 
    onComplete: this.close,
    callbackScope: this
  });
}

 

 

Thanks a lot to you both. I had hoped i could explain myself properly without creating a demo, but not understanding all the terms, make such an endeavour difficult... :)

I did make it work, but as i wrote, properly not correctly.
If i use your example Blake, this.close is simply not being called, nothing happens. 

 

If i type it like this:

 this.close()

 

i get a Runtime Error

 this.close is not a function

 

If i however type this at the beginning of my function, a function which creates the Draggable, and then uses Tween to animate, then it works:

let mainComponent:MainComponent = this;

 

The whole function is here below, this actually works (trial and error), but i would love to make my code even better. This definately feels slightly hacky!

 

updateZone(zone:number, e:any) {
    let selectedElement = undefined;
    let sensor = this.zcon.assignedSensorId[zone]; 
  
    if(sensor && !this.dragNdropModeActive) {
      this.dragNdropModeActive = true;
      this.dragFromThisZone = zone;
      if (e.target.nearestViewportElement.classList.contains('draggable')) {
        selectedElement = e.target.nearestViewportElement;
        
        this.getRect(selectedElement, e);
        this.renderer.addClass(selectedElement, 'dragging');

        let options = false;
        let scale = 0;
        let x = 0;
        let y = 0;

        let mainObject:ZoneControlMain = this; // THIS IS THE WAY I CAN ACCESS MY FUNCTIONS IN TWEEN CALLBACKS
        
        this.draggableZ = Draggable.create(".dragging .z-component", {

          onPress: function() {
            if(!options) {
              options = true;
              scale = this.target._gsTransform.scaleX;
              x =  this.target._gsTransform.x;
              y =  this.target._gsTransform.y;
            }
          },
          onDragStart: function() {
            console.log("Drag start...");
          },
          onDrag: function() {
            const trashCanDropArea = document.getElementsByClassName("trashcan");

            if (this.hitTest(".trashcan", "10%")) {
              if(!trashCanDropArea[0].classList.contains('highlight')) {
                trashCanDropArea[0].classList.add('highlight');
              }
            } else {
              if(trashCanDropArea[0].classList.contains('highlight')) {
                trashCanDropArea[0].classList.remove('highlight');
              }
            }
            if (this.hitTest(".draggable", "70%")) {
              console.log("SWAP");
            }
          },
          onDragEnd: function() {
            if (this.hitTest(".trashcan", "10%")) {
              TweenMax.to(this.target, 0.2, {
                opacity:0, scale:0, 
                onComplete: function() {
                  mainObject.closeDragMode(); // USING THIS ANGULAR COMPONENT TO ACCESS FUNCTIONS OTHERWISE NOT KNOWN TO GREENSOCK
                } 
              });
            }
            if (this.hitTest(".draggable", "70%")) {
              console.log("SWAPPED");
            }
            var tl = new TimelineLite();
            tl.to(this.target, 0.2, {delay: 0.2, x:x, y:y, scale:scale}).to(this.target, 1, {delay: 0.2,opacity:1, ease:Power2.easeInOut});
          }
        });
      }
    } else {
     this.closeDragMode();
    }
  }

 

 

 

 
Link to comment
Share on other sites

7 minutes ago, Balslev said:

If i type it like this:


 this.close()

 

i get a Runtime Error


 this.close is not a function

 

If i however type this at the beginning of my function, a function which creates the Draggable, and then uses Tween to animate, then it works:


let mainComponent:MainComponent = this;

 

 

It doesn't even look like you have a "close" method. You do have a "closeDragMethod" though. 

 

Again, a demo is really the only the way to understand the issue. However, the code you posted gives a little more insight into the problem. It's just a scoping problem.

 

You're not using arrow functions or setting the scope of the draggable, so "this" inside the draggable callbacks refers to the draggable instance. Therefore, this.close() or this.closeDragMethod() will be undefined. So setting "this" to a variable like you did is correct approach. You could still set scope in your callback.

 

onDragEnd: function() {
  if (this.hitTest(".trashcan", "10%")) {
    TweenMax.to(this.target, 0.2, {
      opacity:0, scale:0, 
      onComplete: mainObject.closeDragMode,
      callbackScope: mainObject
    });
  }
  ...
}

 

  • Like 1
Link to comment
Share on other sites

Thanks again for the reply. Sorry about the close() function confusion, i simply typed close to make it shorter for the troubleshooting in an earlier post. The correct name is indeed closeDragMethod().

I am glad my approach is correct!
By setting the callbackScope to my mainObject... will that do anything, or is it more of a "this is the most correct way to do it" approach?

 

I look forward to implementing more awesome Greensock features in our app, and i expect us to join Club Greensock soon, to take advantage of the awesome morphing features.

 


 

Link to comment
Share on other sites

13 minutes ago, Balslev said:

By setting the callbackScope to my mainObject... will that do anything, or is it more of a "this is the most correct way to do it" approach?

 

It eliminates the need to wrap mainObject.closeDragMethod in a function. I wrote a little about scoping here.

 

 

 

If you're curious, here's a demo that shows how I typically set classes up with draggable. Look at the ColorItem class on about line 65. It doesn't use Angular, but it's the same concept. I make the draggable callbacks part of the class.

 

See the Pen VmgNJB by osublake (@osublake) on CodePen

 

 

 

  • Like 2
Link to comment
Share on other sites

  • 10 months later...

I solved this same issue using NgZone. This is not a complete example, but shows that I am invoking the `setPage()` function in my Angular 8 component from the onComplete property in the timeline's .to() declaration. Without the ngZone.run() within the setPage() function, the view doesn't know that the value has changed. With ngZone, it works as intended.

 

enterAnimation( _ElRefSet ) {

  let $nativeEls = this.generateArrayOfNativeElements( _ElRefSet );
  let tl = new TimelineMax();

  tl.to($nativeEls, {
    scale:"1",
    opacity: "1",
    duration:.125,
    stagger: .075,
    onComplete: this.setPage,
    callbackScope: this
  }, "-=.125");

  return tl;
}

setPage() {
  this.ngZone.run( () => {
    this.currentPage++;
  });
}

Love you guys!

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...