简体   繁体   中英

Angular 4 TypeScript callaback with this. in block

I have a theme that I am converting into an Angular 4 admin panel. The theme has a file called app.js containing a class which I am trying to convert into my layout.component.ts In app.js there are several blocks of code that try to access functions outside the class but I have converted all functions into methods of my layout.component.ts ;

$(window).resize(function(){
  this.resizePageContent();
});

Running this gives a Javascript error. However there is a method on my layout.component.ts that should be used in this case.;

this.resizePageContent is not a function

So I am wondering which is the best way to convert this such that the layout.component.ts method is called instead. This is what I have tried but I am not so sure whether this is the best way to do it and why it works.

$(window).resize(()=>{
      this.resizePageContent();
    });

After replacing it with the above code the errors have disappeared.

This is a preview of app.js It has over 700 lines of code so I will not be able to paste it all here;

Update:

var App = function() {
    /* Helper variables - set in uiInit() */
    var page, pageContent, header, footer, sidebar, sScroll, sidebarAlt, sScrollAlt;

    /* Initialization UI Code */
    var uiInit = function() {...};

    /* Page Loading functionality */
    var pageLoading = function(){..};

    /* Gets window width cross browser */
    var getWindowWidth = function(){...};

    /* Sidebar Navigation functionality */
    var handleNav = function() {..};

    /* Scrolls the page (static layout) or the sidebar scroll element (fixed header/sidebars layout) to a specific position - Used when a submenu opens */
    var handlePageScroll = function(sElem, sHeightDiff, sSpeed) {...};

    /* Sidebar Functionality */
    var handleSidebar = function(mode, extra) {...};

    /* Resize #page-content to fill empty space if exists */
    var resizePageContent = function() {...};

    /* Interactive blocks functionality */
    var interactiveBlocks = function() {...};

    /* Scroll to top functionality */
    var scrollToTop = function() {...};

    /* Demo chat functionality (in sidebar) */
    var chatUi = function() {...};

    /* Template Options, change features functionality */
    var templateOptions = function() {...};

    /* Datatables basic Bootstrap integration (pagination integration included under the Datatables plugin in plugins.js) */
    var dtIntegration = function() {...};

    /* Print functionality - Hides all sidebars, prints the page and then restores them (To fix an issue with CSS print styles in webkit browsers)  */
    var handlePrint = function() {...};

    return {
        init: function() {
            uiInit(); // Initialize UI Code
            pageLoading(); // Initialize Page Loading
        },
        sidebar: function(mode, extra) {
            handleSidebar(mode, extra); // Handle sidebars - access functionality from everywhere
        },
        datatables: function() {
            dtIntegration(); // Datatables Bootstrap integration
        },
        pagePrint: function() {
            handlePrint(); // Print functionality
        }
    };
}();

/* Initialize app when page loads */
$(function(){ App.init(); });

This is my layout.component.ts ;

import { Component, OnInit } from '@angular/core';
declare var jQuery: any;
declare var $: any;
declare var window: any;
declare var document: any;
declare  var Cookies: any;

@Component({
  selector: 'app-layout',
  templateUrl: './layout.component.html',
  styleUrls: ['./layout.component.css']
})
export class LayoutComponent implements OnInit {
  public page;
  public pageContent;
  public header;
  public footer;
  public sidebar;
  public sScroll;
  public sidebarAlt;
  public sScrollAlt;

  constructor() { }

  ngOnInit() {
    this.init();
  }

  uiInit (): void {...}

  pageLoading (): void {...}

  getWindowWidth (): any {...}

  handleNav (): any {...}

  handlePageScroll (sElem, sHeightDiff, sSpeed): void {...}

  handleSidebar (mode, extra?:any): any {
    if (mode === 'init') {
      // Init sidebars scrolling functionality
      this.handleSidebar('sidebar-scroll');
      this.handleSidebar('sidebar-alt-scroll');

      // Close the other sidebar if we hover over a partial one
      // In smaller screens (the same applies to resized browsers) two visible sidebars
      // could mess up our main content (not enough space), so we hide the other one :-)
      $('.sidebar-partial #sidebar')
          .mouseenter(function(){ this.handleSidebar('close-sidebar-alt'); });
      $('.sidebar-alt-partial #sidebar-alt')
          .mouseenter(function(){ this.handleSidebar('close-sidebar'); });
    } else {
      var windowW = this.getWindowWidth();

      if (mode === 'toggle-sidebar') {
        if ( windowW > 991) { // Toggle main sidebar in large screens (> 991px)
          this.page.toggleClass('sidebar-visible-lg');

          if (this.page.hasClass('sidebar-mini')) {
            this.page.toggleClass('sidebar-visible-lg-mini');
          }

          if (this.page.hasClass('sidebar-visible-lg')) {
            this.handleSidebar('close-sidebar-alt');
          }

          // If 'toggle-other' is set, open the alternative sidebar when we close this one
          if (extra === 'toggle-other') {
            if (!this.page.hasClass('sidebar-visible-lg')) {
              this.handleSidebar('open-sidebar-alt');
            }
          }
        } else { // Toggle main sidebar in small screens (< 992px)
          this.page.toggleClass('sidebar-visible-xs');

          if (this.page.hasClass('sidebar-visible-xs')) {
            this.handleSidebar('close-sidebar-alt');
          }
        }

        // Handle main sidebar scrolling functionality
        this.handleSidebar('sidebar-scroll');
      }
      else if (mode === 'toggle-sidebar-alt') {
        if ( windowW > 991) { // Toggle alternative sidebar in large screens (> 991px)
          this.page.toggleClass('sidebar-alt-visible-lg');

          if (this.page.hasClass('sidebar-alt-visible-lg')) {
            this.handleSidebar('close-sidebar');
          }

          // If 'toggle-other' is set open the main sidebar when we close the alternative
          if (extra === 'toggle-other') {
            if (!this.page.hasClass('sidebar-alt-visible-lg')) {
              this.handleSidebar('open-sidebar');
            }
          }
        } else { // Toggle alternative sidebar in small screens (< 992px)
          this.page.toggleClass('sidebar-alt-visible-xs');

          if (this.page.hasClass('sidebar-alt-visible-xs')) {
            this.handleSidebar('close-sidebar');
          }
        }
      }
      else if (mode === 'open-sidebar') {
        if ( windowW > 991) { // Open main sidebar in large screens (> 991px)
          if (this.page.hasClass('sidebar-mini')) { this.page.removeClass('sidebar-visible-lg-mini'); }
          this.page.addClass('sidebar-visible-lg');
        } else { // Open main sidebar in small screens (< 992px)
          this.page.addClass('sidebar-visible-xs');
        }

        // Close the other sidebar
        this.handleSidebar('close-sidebar-alt');
      }
      else if (mode === 'open-sidebar-alt') {
        if ( windowW > 991) { // Open alternative sidebar in large screens (> 991px)
          this.page.addClass('sidebar-alt-visible-lg');
        } else { // Open alternative sidebar in small screens (< 992px)
          this.page.addClass('sidebar-alt-visible-xs');
        }

        // Close the other sidebar
        this.handleSidebar('close-sidebar');
      }
      else if (mode === 'close-sidebar') {
        if ( windowW > 991) { // Close main sidebar in large screens (> 991px)
          this.page.removeClass('sidebar-visible-lg');
          if (this.page.hasClass('sidebar-mini')) { this.page.addClass('sidebar-visible-lg-mini'); }
        } else { // Close main sidebar in small screens (< 992px)
          this.page.removeClass('sidebar-visible-xs');
        }
      }
      else if (mode === 'close-sidebar-alt') {
        if ( windowW > 991) { // Close alternative sidebar in large screens (> 991px)
          this.page.removeClass('sidebar-alt-visible-lg');
        } else { // Close alternative sidebar in small screens (< 992px)
          this.page.removeClass('sidebar-alt-visible-xs');
        }
      }
      else if (mode === 'sidebar-scroll') { // Handle main sidebar scrolling
        if (this.page.hasClass('sidebar-mini') && this.page.hasClass('sidebar-visible-lg-mini') && (windowW > 991)) { // Destroy main sidebar scrolling when in mini sidebar mode
          if (this.sScroll.length && this.sScroll.parent('.slimScrollDiv').length) {
            this.sScroll
                .slimScroll({destroy: true});
            this.sScroll
                .attr('style', '');
          }
        }
        else if ((this.page.hasClass('header-fixed-top') || this.page.hasClass('header-fixed-bottom'))) {
          var sHeight = $(window).height();

          if (this.sScroll.length && (!this.sScroll.parent('.slimScrollDiv').length)) { // If scrolling does not exist init it..
            this.sScroll
                .slimScroll({
                  height: sHeight,
                  color: '#fff',
                  size: '3px',
                  touchScrollStep: 100
                });

            // Handle main sidebar's scrolling functionality on resize or orientation change
            var sScrollTimeout;

            $(window).on('resize orientationchange', function(){
              clearTimeout(sScrollTimeout);

              sScrollTimeout = setTimeout(function(){
                this.handleSidebar('sidebar-scroll');
              }, 150);
            });
          }
          else { // ..else resize scrolling height
            this.sScroll
                .add(this.sScroll.parent())
                .css('height', sHeight);
          }
        }
      }
      else if (mode === 'sidebar-alt-scroll') { // Init alternative sidebar scrolling
        if ((this.page.hasClass('header-fixed-top') || this.page.hasClass('header-fixed-bottom'))) {
          var sHeightAlt = $(window).height();

          if (this.sScrollAlt.length && (!this.sScrollAlt.parent('.slimScrollDiv').length)) { // If scrolling does not exist init it..
            this.sScrollAlt
                .slimScroll({
                  height: sHeightAlt,
                  color: '#fff',
                  size: '3px',
                  touchScrollStep: 100
                });

            // Resize alternative sidebar scrolling height on window resize or orientation change
            var sScrollAltTimeout;

            $(window).on('resize orientationchange', function(){
              clearTimeout(sScrollAltTimeout);

              sScrollAltTimeout = setTimeout(function(){
                this.handleSidebar('sidebar-alt-scroll');
              }, 150);
            });
          }
          else { // ..else resize scrolling height
            this.sScrollAlt
                .add(this.sScrollAlt.parent())
                .css('height', sHeightAlt);
          }
        }
      }
    }

    return false;
  }

  resizePageContent (): void {...}

  interactiveBlocks (): void {...}

  scrollToTop (): any{...}

  chatUi (): any {...}

  templateOptions (): void {...}

  dtIntegration (): any {...}

  handlePrint (): void {...}


  //Methods from original object
  init (): void {
    this.uiInit(); // Initialize UI Code
    this.pageLoading(); // Initialize Page Loading
  }
  //Originally sidebar
  CallhandleSidebar(mode, extra): void {
    this.handleSidebar(mode, extra); // Handle sidebars - access functionality from everywhere
  }

  datatables() :void {
    this.dtIntegration(); // Datatables Bootstrap integration
  }

  pagePrint (): void {
    this.handlePrint(); // Print functionality
  }

}

Also could you give me an example of how TypeScript might output something like this;

$(window).resize(()=>{
  this.resizePageContent();
});

...just to get a good idea of how it works.

In your case, using the code

$(window).resize(()=>{
  this.resizePageContent();
});

is the correct way for accessing functions and variables outside of the scope of the callback function.

When using

function() {
  this.something;
}

the 'this' is bound to the scope of the function, rather than of the class.

When using

() => {
    this.something
}

that is using ecmascript 6 arrow notation. Ecmascript 6 sees the introduction of the lexical this, where in the latter case the 'this' keyword refers to the class defined.

For more information see http://es6-features.org/#Lexicalthis and https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_binding_of_this .

If you have to do just delegate or one line coder in the callback, go for this:

$(window).resize(()=> this.resizePageContent());

Or you can use the following too resize the page content:

another question on stackoverflow to catch the window resizing event in Angular context

and make your resizePageContent() method to listen for that event.

Opinion based question, but personally the best way is to use a class field and assign an arrow function definition, and to -not- use jQuery while using angular:

window.addEventListener('resize', this.resizePageContent);

Within your class you should then define the method like this:

private resizePageContent: EventListener = (event: UIEvent): void => {

};

The big advantage of this, is that you can remove the event listener, which is impossible if you use an anonymous arrow function.

plunkr

Another option is to use bind(this) to retain the this context:

window.addEventListener('resize', this.resizePageContent.bind(this));

This way you do not need to assign your callback to a field of the class. But using bind creates a copy of the method, which makes it harder (not impossible) to remove the event listener

You can also just immediately listen to it from within your component using @HostListener . Removing of the event is done internally by angular on destroy of the component

@HostListener('window:resize', ['$event'])
public resizePageContent(event: Event) {}

The reason you were getting an error is because you were using the function keyword. This changes the this context to the anonymous function you are defining as callback

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM