0

http://localhost:4200-console errToday I am facing below err in console after building angular 16 app with 'ng serve' without any build err. Basically I am trying to upgrade from angular 7 to angular 16.So we were using angular 7 + fuse-perfect-scrollbar.But now its ngx-perfect-scrollbar is deprecated.So we decided to use ng-scrollbar .The below code block taken from github -fuse-perfect-scrollbar.directive.ts file .As perfect-scrollbar deprecated the code related to it commententd and NGScrollbar code is added as below

   import { AfterViewInit, Directive, ElementRef, HostListener, Input, NgZone, OnDestroy } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { Platform } from '@angular/cdk/platform';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
import { NgScrollbar, NgScrollbarModule, ScrollbarManager } from 'ngx-scrollbar';//'perfect-scrollbar';
import * as _ from 'lodash';

import { FuseConfigService } from '../../../@fuse/services/config.service';
import { NgLocaleLocalization, NgLocalization } from '@angular/common';
import { Directionality } from '@angular/cdk/bidi';
import { SmoothScroll, SmoothScrollManager } from 'ngx-scrollbar/smooth-scroll';

@Directive({
    selector: '[fusePerfectScrollbar]'
})
export class FusePerfectScrollbarDirective implements AfterViewInit, OnDestroy {
    isInitialized: boolean;
    isMobile: boolean;
    ps: NgScrollbar | any;

    // Private
    private _enabled: boolean | '';
    private _debouncedUpdate: any;
    private _options: any;
    private _unsubscribeAll: Subject<any>;
    private _sensorDisabled : boolean | '';
    /**
     * Constructor
     *
     * @param {ElementRef} elementRef
     * @param {FuseConfigService} _fuseConfigService
     * @param {Platform} _platform
     * @param {Router} _router
     */
    constructor(
        public elementRef: ElementRef,
        private _fuseConfigService: FuseConfigService,
        private _platform: Platform,
        private _router: Router
    ) {
        // Set the defaults
        this.isInitialized = false;
        this.isMobile = false;

        // Set the private defaults
        this._enabled = false;
        this._sensorDisabled = true;
        this._debouncedUpdate = _.debounce(this.update, 150);
        this._options = {
            updateOnRouteChange: false
        };
        this._unsubscribeAll = new Subject();
    }

    // -----------------------------------------------------------------------------------------------------
    // @ Accessors
    // -----------------------------------------------------------------------------------------------------

    /**
     * Perfect Scrollbar options
     *
     * @param value
     */
    @Input()
    set fusePerfectScrollbarOptionsvalue(value : any) {
        // Merge the options
        this._options = _.merge({}, this._options, value,this._sensorDisabled);
    }

    get fusePerfectScrollbarOptions(): any {
        // Return the options
        return this._options;
    }

    /**
     * Is enabled
     *
     * @param {boolean | ""} value
     */
    @Input('fusePerfectScrollbar')
    set enabled(value: boolean | '') {
        // If nothing is provided with the directive (empty string),
        // we will take that as a true
        if (value === '') {
            value = true;
        }

        // Return, if both values are the same
        if (this.enabled === value) {
            return;
        }

        // Store the value
        this._enabled = value;

        // If enabled...
        if (this.enabled) {
            // Init the directive
            this._init();
        }
        else {
            // Otherwise destroy it
            this._destroy();
        }
    }

    get enabled(): boolean | '' {
        // Return the enabled status
        return this._enabled;
    }

    // -----------------------------------------------------------------------------------------------------
    // @ Lifecycle hooks
    // -----------------------------------------------------------------------------------------------------

    /**
     * After view init
     */
    ngAfterViewInit(): void {
        // Check if scrollbars enabled or not from the main config
        this._fuseConfigService.config
            .pipe(takeUntil(this._unsubscribeAll))
            .subscribe(
                (settings: { customScrollbars: string | boolean; }) => {
                    this.enabled = Boolean(settings.customScrollbars);
                }
            );

        // Scroll to the top on every route change
        if (this.fusePerfectScrollbarOptions.updateOnRouteChange) {
            this._router.events
                .pipe(
                    takeUntil(this._unsubscribeAll),
                    filter(event => event instanceof NavigationEnd)
                )
                .subscribe(() => {
                    setTimeout(() => {
                        this.scrollToTop();
                        this.update();
                    }, 0);
                });
        }
    }

    /**
     * On destroy
     */
    ngOnDestroy(): void {
        this._destroy();

        // Unsubscribe from all subscriptions
        this._unsubscribeAll.next(this);
        this._unsubscribeAll.complete();
    }

    // -----------------------------------------------------------------------------------------------------
    // @ Private methods
    // -----------------------------------------------------------------------------------------------------

    /**
     * Initialize
     *
     * @private
     */
    _init(): void {
        // Return, if already initialized
        if (this.isInitialized) {
            return;
        }

        // Check if is mobile
        if (this._platform.ANDROID || this._platform.IOS) {
            this.isMobile = true;
        }

        // Return if it's mobile
        if (this.isMobile) {
            // Return...
            return;
        }

        // Set as initialized
        this.isInitialized = true;

        // Initialize the perfect-scrollbar
        // this.ps = new PerfectScrollbar(this.elementRef.nativeElement, {
        //     ...this.fusePerfectScrollbarOptions
        // });
        // Initialize the ngx-scrollbar
        
        this.ps = new NgScrollbar(this.elementRef.nativeElement
            , NgZone.bind(this.fusePerfectScrollbarOptions), 
            this.fusePerfectScrollbarOptions,Directionality.prototype,
             SmoothScrollManager.bind(this.fusePerfectScrollbarOptions),
             ScrollbarManager.bind(this.fusePerfectScrollbarOptions),
            
            
        );

        // Unbind 'keydown' events of PerfectScrollbar since it causes an extremely
        // high CPU usage on Angular Material inputs.
        // Loop through all the event elements of this PerfectScrollbar instance
        this.ps.event.eventElements.forEach((eventElement: { handlers: { [x: string]: any[]; }; element: { removeEventListener: (arg0: string, arg1: any) => void; }; }) => {

            // If we hit to the element with a 'keydown' event...
            if (typeof eventElement.handlers['keydown'] !== 'undefined') {
                // Unbind it
                eventElement.element.removeEventListener('keydown', eventElement.handlers['keydown'][0]);
            }
        });
    }

    /**
     * Destroy
     *
     * @private
     */
    _destroy(): void {
        if (!this.isInitialized || !this.ps) {
            return;
        }

        // Destroy the perfect-scrollbar
        this.ps.destroy();

        // Clean up
        this.ps = null;
        this.isInitialized = false;
    }

    /**
     * Update scrollbars on window resize
     *
     * @private
     */
    @HostListener('window:resize')
    _updateOnResize(): void {
        this._debouncedUpdate();
    }

    // -----------------------------------------------------------------------------------------------------
    // @ Public methods
    // -----------------------------------------------------------------------------------------------------

    /**
     * Document click
     *
     * @param {Event} event
     */
    @HostListener('document:click', ['$event'])
    documentClick(event: Event): void {
        if (!this.isInitialized || !this.ps) {
            return;
        }

        // Update the scrollbar on document click..
        // This isn't the most elegant solution but there is no other way
        // of knowing when the contents of the scrollable container changes.
        // Therefore, we update scrollbars on every document click.
        this.ps.update();
    }

    /**
     * Update the scrollbar
     */
    update(): void {
        if (!this.isInitialized) {
            return;
        }

        // Update the perfect-scrollbar
        this.ps.update();
    }

    /**
     * Destroy the scrollbar
     */
    destroy(): void {
        this.ngOnDestroy();
    }

    /**
     * Scroll to X
     *
     * @param {number} x
     * @param {number} speed
     */
    scrollToX(x: number, speed?: number): void {
        this.animateScrolling('scrollLeft', x, speed);
    }

    /**
     * Scroll to Y
     *
     * @param {number} y
     * @param {number} speed
     */
    scrollToY(y: number, speed?: number): void {
        this.animateScrolling('scrollTop', y, speed);
    }

    /**
     * Scroll to top
     *
     * @param {number} offset
     * @param {number} speed
     */
    scrollToTop(offset?: number, speed?: number): void {
        this.animateScrolling('scrollTop', (offset || 0), speed);
    }

    /**
     * Scroll to left
     *
     * @param {number} offset
     * @param {number} speed
     */
    scrollToLeft(offset?: number, speed?: number): void {
        this.animateScrolling('scrollLeft', (offset || 0), speed);
    }

    /**
     * Scroll to right
     *
     * @param {number} offset
     * @param {number} speed
     */
    scrollToRight(offset?: number, speed?: number): void {
        const width = this.elementRef.nativeElement.scrollWidth;

        this.animateScrolling('scrollLeft', width - (offset || 0), speed);
    }

    /**
     * Scroll to bottom
     *
     * @param {number} offset
     * @param {number} speed
     */
    scrollToBottom(offset?: number, speed?: number): void {
        const height = this.elementRef.nativeElement.scrollHeight;

        this.animateScrolling('scrollTop', height - (offset || 0), speed);
    }

    /**
     * Animate scrolling
     *
     * @param {string} target
     * @param {number} value
     * @param {number} speed
     */
    animateScrolling(target: string, value: number, speed?: number): void {
        if (!speed) {
            this.elementRef.nativeElement[target] = value;

            // PS has weird event sending order, this is a workaround for that
            this.update();
            this.update();
        }
        else if (value !== this.elementRef.nativeElement[target]) {
            let newValue = 0;
            let scrollCount = 0;

            let oldTimestamp = performance.now();
            let oldValue = this.elementRef.nativeElement[target];

            const cosParameter = (oldValue - value) / 2;

            const step = (newTimestamp: number) => {
                scrollCount += Math.PI / (speed / (newTimestamp - oldTimestamp));

                newValue = Math.round(value + cosParameter + cosParameter * Math.cos(scrollCount));

                // Only continue animation if scroll position has not changed
                if (this.elementRef.nativeElement[target] === oldValue) {
                    if (scrollCount >= Math.PI) {
                        this.elementRef.nativeElement[target] = value;

                        // PS has weird event sending order, this is a workaround for that
                        this.update();

                        this.update();
                    }
                    else {
                        this.elementRef.nativeElement[target] = oldValue = newValue;

                        oldTimestamp = newTimestamp;

                        window.requestAnimationFrame(step);
                    }
                }
            };

            window.requestAnimationFrame(step);
        }
    }
}


So the question is if Perfect-Scrollbar is deprecated then what could be the replacement of it. Also if we can use NgScrollbar then what is the best way to use it to avoid above runtime err in console. Any help will be appreciated.

> properties of undefined (reading 'sensorDisabled')
    at new NgScrollbar (ngx-scrollbar.mjs:880:59)
    at _a5._init (fuse-perfect-scrollbar.directive.ts:192:19)
    at set enabled [as enabled]
Anushweta
  • 21
  • 5

0 Answers0