-1

Having trouble mixing some version of JavaScript in my Angular Typescript file.

In the code below, in ngOnInit, the function globalNavigationHandler is not recongnized and I cannot use this.globalNavigationHandler because this has a different meaning which I need to keep.

I have an eventlistener that is not an arrow function because I need the THIS rebound.

However, I do not know how to properly get the function to recognize the other function that I have called globalnavigationHandler. I cannot put globalNavigationHandler inline because it needs the router which is injected into the constructor and has a different meaning of this.

globalNavigationHandler = function(path) {
    this.router.navigate([path]);
  }

  ngOnInit() {
    var nav = document.getElementById("uhf-c-nav");
    var anchors = nav.getElementsByTagName("a");

    for (var i = 0; i < anchors.length; i++) {
      anchors[i].addEventListener(
        "click",
        function(event) {
          event.preventDefault();
          console.log("this is the element the event is bound to: " + this.id);
          console.log("the event target is the clicked element: " + event);
          console.log("this: " + this);
          console.log("this.pathname: " + this.pathname);

          globalNavigationHandler(this.pathname);
        },
        false
      );
    }
  }

Thanks

Jordan McDonald
  • 1,101
  • 3
  • 14
  • 24

2 Answers2

0
globalNavigationHandler(path) {
   this.router.navigate([path]);
}

Try simply declaring it like this.

EDIT - Normally I'd provide a better explanation... but I don't have much experience with 'vanilla' javascript. Maybe someone else can explain why this is the case. I just know this is how you declare functions inside classes in typescript. You can use the notation you had in your original post if you are declaring a function inside another function, but a class function is declared without the = assignment and function keyword.

ngOnInit() {
    var nav = document.getElementById("uhf-c-nav");
    var anchors = nav.getElementsByTagName("a");

    for (let anch of anchors) {
      anch.addEventListener(
        "click",
        (event) => {
          event.preventDefault();
          console.log("this is the element the event is bound to: " + anch.id);
          console.log("the event target is the clicked element: " + event);
          console.log("anch: " + anch);
          console.log("anch.pathname: " + anch.pathname);

          this.globalNavigationHandler(anch.pathname);
        },
        false
      );
    }
diopside
  • 2,981
  • 11
  • 23
  • Updated my answer with how the rest should look. If you're not going to store 'this' as a variable like estus recommended below, you'll have to use a fat arrow function to keep the context and refer to the anchors differently (like he also mentioned) – diopside Aug 27 '17 at 13:25
  • Here's a plunkr demonstrating - https://plnkr.co/edit/zaWfU4KGG708tOZnlztj – diopside Aug 27 '17 at 13:39
0

globalNavigationHandler should be class method because it's semantically correct, and it still requires to be bound to proper this in order to get this.router. If there's a problem with reaching class method as this.globalNavigationHandler, it should be solved in another way.

There are several recipes to approach the case when there are two contexts in use. The most popular and simple way to do this is assigning one of them to variable:

const self = this;
...

  anchors[i].addEventListener(
    "click",
    function(event) {
      ...
      self.globalNavigationHandler(this.pathname);
    },
    false
  );

In practice it is rarely needed because it's a good practice for callback callers to not rely on this and provide all necessary information in callback arguments. In event listeners it is event.target which is equal to this in the code above. It should be instead:

  anchors[i].addEventListener(
    "click",
    event => {
      ...
      this.globalNavigationHandler(event.target.pathname);
    },
    false
  );

It appears that this is XY problem that should be addressed in a way that is natural to Angular. Usually you never need to use addEventListener directly in Angular. Renderer2 provider is an abstraction that is used for DOM-related operations, like is shown here, and element references should be retrieved with ViewChild/ViewChildren.

If it is possible to make these elements set up click listeners by themselves with directives and not from a parent, this is preferable approach.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565