angular - मार्ग परिवर्तन पर शीर्ष पर स्क्रॉल 2 कोणीय




typescript angular2-routing (14)

अपने कोणीय 2 ऐप में जब मैं किसी पृष्ठ को नीचे स्क्रॉल करता हूं और पृष्ठ के निचले भाग में लिंक पर क्लिक करता हूं, तो यह मार्ग बदल देता है और मुझे अगले पृष्ठ पर ले जाता है लेकिन यह पृष्ठ के शीर्ष पर स्क्रॉल नहीं करता है। परिणामस्वरूप, यदि पहला पृष्ठ लंबा है और दूसरे पृष्ठ में कुछ सामग्री है, तो यह धारणा देता है कि 2 पृष्ठ में सामग्री का अभाव है। चूंकि सामग्री केवल तभी दिखाई देती है जब उपयोगकर्ता पृष्ठ के शीर्ष पर स्क्रॉल करता है।

मैं घटक के ngInit में पृष्ठ के शीर्ष पर विंडो स्क्रॉल कर सकता हूं लेकिन, क्या कोई बेहतर उपाय है जो अपने ऐप में सभी मार्गों को स्वचालित रूप से संभाल सकता है?


@ फर्नांडो एचेवरिया महान! लेकिन यह कोड हैश राउटर या आलसी राउटर में काम नहीं करता है। क्योंकि वे स्थान परिवर्तन को ट्रिगर नहीं करते हैं। यह कोशिश कर सकते हैं:

import {Directive, HostListener} from '@angular/core';

@Directive({
    selector: '[linkToTop]'
})
export class LinkToTopDirective {

    @HostListener('click')
    onClick(): void {
        window.scrollTo(0, 0);
    }
}


Angular 6.1 से, अब आप परेशानी से बच सकते हैं और अपने RouterModule.forRoot() को एक दूसरे पैरामीटर के रूप में RouterModule.forRoot() पास कर सकते हैं और extraOptions को निर्दिष्ट कर सकते हैं scrollPositionRestoration: enabled जब भी मार्ग में परिवर्तन होता है, तो स्क्रॉल करने के लिए Angular को बताने में scrollPositionRestoration: enabled

डिफ़ॉल्ट रूप से आपको यह app-routing.module.ts में मिलेगा:

const routes: Routes = [
  {
    path: '...'
    component: ...
  },
  ...
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, {
      scrollPositionRestoration: 'enabled', // Add options right here
    })
  ],
  exports: [RouterModule]
})
export class AppRoutingModule { }

कोणीय आधिकारिक डॉक्स


आप अपने घटक में AfterViewInit जीवनचक्र हुक जोड़ सकते हैं।

ngAfterViewInit() {
   window.scrollTo(0, 0);
}

आप अपने मुख्य घटक पर मार्ग परिवर्तन श्रोता को पंजीकृत कर सकते हैं और मार्ग परिवर्तन पर शीर्ष पर स्क्रॉल कर सकते हैं।

import { Component, OnInit } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';

@Component({
    selector: 'my-app',
    template: '<ng-content></ng-content>',
})
export class MyAppComponent implements OnInit {
    constructor(private router: Router) { }

    ngOnInit() {
        this.router.events.subscribe((evt) => {
            if (!(evt instanceof NavigationEnd)) {
                return;
            }
            window.scrollTo(0, 0)
        });
    }
}

इस कोड के पीछे मुख्य विचार सभी विज़िट किए गए यूआरएल को एक सरणी में संबंधित स्क्रॉल डेटा के साथ रखना है। जब भी कोई उपयोगकर्ता किसी पृष्ठ को छोड़ता है (नेविगेशनस्टार्ट) तो यह सरणी अद्यतन हो जाती है। जब भी कोई उपयोगकर्ता किसी नए पृष्ठ (नेविगेशनईंड) में प्रवेश करता है, तो हम वाई स्थिति को पुनर्स्थापित करने का निर्णय लेते हैं या इस पृष्ठ पर हमें कैसे प्राप्त होते हैं, इस पर निर्भर नहीं करता है। यदि किसी पृष्ठ पर एक रेफ़रेन्स का उपयोग किया गया था तो हम 0. पर स्क्रॉल करते थे। यदि ब्राउज़र बैक / फॉरवर्ड फीचर्स का उपयोग किया जाता था तो हम अपने एरे में सेव किए गए वाई स्क्रॉल करते हैं। मेरी अंग्रेजी के लिए खेद है :)

this.router.events.subscribe((evt) => {
   if (evt instanceof NavigationEnd) {
      document.body.scrollTop = 0;
   }
});

कोणीय 6.1 के रूप में, राउटर scrollPositionRestoration नाम से एक कॉन्फ़िगरेशन विकल्प प्रदान करता है, यह इस परिदृश्य के लिए पूरा करने के लिए डिज़ाइन किया गया है।

export class AppComponent implements OnInit {
  isPopState = false;

  constructor(private router: Router, private locStrat: LocationStrategy) { }

  ngOnInit(): void {
    this.locStrat.onPopState(() => {
      this.isPopState = true;
    });

    this.router.events.subscribe(event => {
      // Scroll to top if accessing a page, not via browser history stack
      if (event instanceof NavigationEnd && !this.isPopState) {
        window.scrollTo(0, 0);
        this.isPopState = false;
      }

      // Ensures that isPopState is reset
      if (event instanceof NavigationEnd) {
        this.isPopState = false;
      }
    });
  }
}

नीचे दिखाए गए अनुसार द्वारा प्रदान किए गए सही उत्तर के अलावा, आप नीचे दिखाए गए चिकनी स्क्रॉल जोड़कर अपने कार्यान्वयन को कर सकते हैं

setTimeout(function(){
    window.scrollTo(0, 1);
}, 0);

फिर नीचे स्निपेट जोड़ें

.wrapper(#outlet="")
    router-outlet((activate)='routerActivate($event,outlet)')

आपकी शैलियों के लिए


बस क्लिक कार्रवाई के साथ यह आसान है

अपने मुख्य घटक में html संदर्भ #scrollContainer बनाते हैं

<div class="main-container" #scrollContainer>
    <router-outlet (activate)="onActivate($event, scrollContainer)"></router-outlet>
</div>

मुख्य घटक में .ts

onActivate(e, scrollContainer) {
    scrollContainer.scrollTop = 0;
}

यदि आपको शीर्ष पर केवल स्क्रॉल पृष्ठ की आवश्यकता है, तो आप ऐसा कर सकते हैं (सबसे अच्छा समाधान नहीं है, लेकिन तेजी से)

imports: [
  RouterModule.forRoot(routes, {
    scrollPositionRestoration: 'enabled'
  }),
  ...
]

यह समाधान @ फर्नांडोएचेवरिया और @ गुइलहर्मेइरेलेस के समाधान पर आधारित है, लेकिन यह अधिक संक्षिप्त है और पॉपस्टैट तंत्र के साथ काम करता है जो कोणीय राउटर प्रदान करता है। यह कई लगातार नेविगेट के स्क्रॉल स्तर को संग्रहीत करने और पुनर्स्थापित करने की अनुमति देता है।

हम प्रत्येक नेविगेशन स्थिति के लिए स्क्रॉल स्थिति को मैप स्क्रॉल में संग्रहीत करते हैं। एक बार पॉपस्टैट घटना होने पर, राज्य की आईडी जो बहाल होने वाली है, उसे event.restoredState.navigationId राउटर: event.restoredState.navigationId द्वारा आपूर्ति की जाती है। इसके बाद scrollLevels से उस राज्य का अंतिम स्क्रॉल स्तर प्राप्त करने के लिए इसका उपयोग किया जाता है।

यदि मार्ग के लिए कोई संग्रहीत स्क्रॉल स्तर नहीं है, तो यह शीर्ष पर स्क्रॉल करेगा जैसा कि आप अपेक्षा करेंगे।

import { Component, OnInit } from '@angular/core';
import { Router, NavigationStart, NavigationEnd } from '@angular/router';

@Component({
    selector: 'my-app',
    template: '<ng-content></ng-content>',
})
export class AppComponent implements OnInit {

  constructor(private router: Router) { }

  ngOnInit() {
    const scrollLevels: { [navigationId: number]: number } = {};
    let lastId = 0;
    let restoredId: number;

    this.router.events.subscribe((event: Event) => {

      if (event instanceof NavigationStart) {
        scrollLevels[lastId] = window.scrollY;
        lastId = event.id;
        restoredId = event.restoredState ? event.restoredState.navigationId : undefined;
      }

      if (event instanceof NavigationEnd) {
        if (restoredId) {
          // Optional: Wrap a timeout around the next line to wait for
          // the component to finish loading
          window.scrollTo(0, scrollLevels[restoredId] || 0);
        } else {
          window.scrollTo(0, 0);
        }
      }

    });
  }

}

यहाँ एक समाधान है जो मैं लेकर आया हूँ। मैंने राउटर इवेंट्स के साथ लोकेशनस्ट्रेगी को जोड़ा। किसी उपयोगकर्ता द्वारा वर्तमान में ब्राउज़र इतिहास के माध्यम से पता लगाने पर बूलियन सेट करने के लिए LocationStrategy का उपयोग करना। इस तरह, मुझे URL और y- स्क्रॉल डेटा का एक गुच्छा संग्रहीत करने की आवश्यकता नहीं है (जो वैसे भी अच्छी तरह से काम नहीं करता है, क्योंकि प्रत्येक डेटा को URL के आधार पर बदल दिया जाता है)। यह उस किनारे के मामले को भी हल करता है जब कोई उपयोगकर्ता किसी ब्राउज़र पर बैक या फॉरवर्ड बटन को होल्ड करने का निर्णय लेता है और केवल एक के बजाय कई पेजों को वापस या फॉरवर्ड करता है।

PS मैंने केवल IE, क्रोम, फ़ायरफ़ॉक्स, सफारी और ओपेरा (इस पोस्ट के रूप में) के नवीनतम संस्करण पर परीक्षण किया है।

उम्मीद है की यह मदद करेगा।

document.getElementById('elementId').scrollTop = 0;

सबसे अच्छा उत्तर एंगुलर गिटहब चर्चा में रहता है ( बदलते मार्ग नए पृष्ठ में शीर्ष पर स्क्रॉल नहीं करता है )।

हो सकता है कि आप केवल रूट राउटर परिवर्तनों में ही शीर्ष पर जाना चाहते हैं (बच्चों में नहीं, क्योंकि आप एक टैब में आलसी लोड वाले मार्गों को लोड कर सकते हैं)

app.component.html

<router-outlet (deactivate)="onDeactivate()"></router-outlet>

app.component.ts

onDeactivate() {
  document.body.scrollTop = 0;
  // Alternatively, you can scroll to top by using this other call:
  // window.scrollTo(0, 0)
}

JoniJnm लिए पूर्ण क्रेडिट ( मूल पोस्ट )


Router का उपयोग करने से उन मुद्दों का कारण होगा जो आप लगातार ब्राउज़र अनुभव बनाए रखने के लिए पूरी तरह से दूर नहीं कर सकते हैं। मेरी राय में सबसे अच्छा तरीका सिर्फ एक कस्टम directive उपयोग करना है और इसे क्लिक पर स्क्रॉल रीसेट करने देना है। इसके बारे में अच्छी बात यह है कि यदि आप उसी url पर हैं जिस पर आप क्लिक करते हैं, तो पृष्ठ शीर्ष पर भी वापस स्क्रॉल करेगा। यह सामान्य वेबसाइटों के अनुरूप है। मूल directive कुछ इस तरह दिख सकता है:

<a routerLink="/" linkToTop></a>

निम्नलिखित उपयोग के साथ:

@Directive({
  selector: '[linkToTop]'
})
export class LinkToTopDirective implements OnInit, OnDestroy {

  @Input()
  set linkToTop(active: string | boolean) {
    this.active = typeof active === 'string' ? active.length === 0 : active;
  }

  private active: boolean = true;

  private onClick: EventListener = (event: MouseEvent) => {
    if (this.active) {
      window.scrollTo(0, 0);
    }
  };

  constructor(@Inject(PLATFORM_ID) private readonly platformId: Object,
              private readonly elementRef: ElementRef,
              private readonly ngZone: NgZone
  ) {}

  ngOnDestroy(): void {
    if (isPlatformBrowser(this.platformId)) {
      this.elementRef.nativeElement.removeEventListener('click', this.onClick, false);
    }
  }

  ngOnInit(): void {
    if (isPlatformBrowser(this.platformId)) {
      this.ngZone.runOutsideAngular(() => 
        this.elementRef.nativeElement.addEventListener('click', this.onClick, false)
      );
    }
  }
}

यह अधिकांश उपयोग के मामलों के लिए पर्याप्त होगा, लेकिन मैं कुछ मुद्दों की कल्पना कर सकता हूं जो इससे उत्पन्न हो सकते हैं:

  • window के उपयोग के कारण universal पर काम नहीं करता है
  • परिवर्तन का पता लगाने पर छोटे गति का प्रभाव, क्योंकि यह हर क्लिक से चालू होता है
  • इस निर्देश को अक्षम करने का कोई तरीका नहीं है

इन मुद्दों को दूर करना वास्तव में काफी आसान है:

<a routerLink="/" linkToTop></a> <!-- always active -->
<a routerLink="/" [linkToTop]="isActive"> <!-- active when `isActive` is true -->

यह अधिकांश उपयोग-मामलों को ध्यान में रखता है, मूल रूप में एक ही उपयोग के साथ, सक्षम / अक्षम करने के लाभ के साथ:

@Directive({
    selector: '[linkToTop]'
})
export class LinkToTopDirective {

    @Input()
    set linkToTop(active: string|boolean) {
        this.active = typeof active === 'string' ? active.length === 0 : active;
    }

    private active: boolean = true;

    @HostListener('click.pnb')
    onClick(): void {
      if (this.active) {
        window.scrollTo(0, 0);
      }        
    }
}

यदि आप विज्ञापित नहीं होना चाहते हैं तो विज्ञापन न पढ़ें

एक और सुधार यह जांचने के लिए किया जा सकता है कि ब्राउज़र passive घटनाओं का समर्थन करता है या नहीं। यह कोड को थोड़ा और जटिल करेगा, और थोड़ा अस्पष्ट है यदि आप इन सभी को अपने कस्टम निर्देश / टेम्प्लेट में लागू करना चाहते हैं। इसलिए मैंने एक छोटी सी library लिखी जिसका उपयोग आप इन समस्याओं को दूर करने के लिए कर सकते हैं। ऊपर की तरह ही कार्यक्षमता होने के लिए, और अतिरिक्त passive घटना के साथ, यदि आप ng-event-options लाइब्रेरी ng-event-options उपयोग करते हैं, तो आप इसके निर्देश को बदल सकते हैं। तर्क click.pnb श्रोता के अंदर है:

constructor(private route: ActivatedRoute) {}

ngOnInit() {
  this._sub = this.route.fragment.subscribe((hash: string) => {
    if (hash) {
      const cmp = document.getElementById(hash);
      if (cmp) {
        cmp.scrollIntoView();
      }
    } else {
      window.scrollTo(0, 0);
    }
  });
}

window.scrollTo() मेरे लिए Angular 5 में काम नहीं करता है, इसलिए मैंने document.body.scrollTop उपयोग किया है, जैसे

 this.router.events.subscribe((evt) => { if (evt instanceof NavigationEnd) { document.body.scrollTop = 0; } }); 




angular2-directives