Angular 2'de rotalar arasında gezinirken yükleme ekranını göster


Yanıtlar:


196

Mevcut Angular Router, Navigasyon Olayları sağlar. Bunlara abone olabilir ve buna göre UI değişiklikleri yapabilirsiniz. Gibi diğer Olaylar saymak unutmayın NavigationCancelve NavigationErrorgeçişler başarısız yönlendirici ihtimaline karşı değer değişimi durdurmak için.

app.component.ts - kök bileşeniniz

...
import {
  Router,
  // import as RouterEvent to avoid confusion with the DOM Event
  Event as RouterEvent,
  NavigationStart,
  NavigationEnd,
  NavigationCancel,
  NavigationError
} from '@angular/router'

@Component({})
export class AppComponent {

  // Sets initial value to true to show loading spinner on first load
  loading = true

  constructor(private router: Router) {
    this.router.events.subscribe((e : RouterEvent) => {
       this.navigationInterceptor(e);
     })
  }

  // Shows and hides the loading spinner during RouterEvent changes
  navigationInterceptor(event: RouterEvent): void {
    if (event instanceof NavigationStart) {
      this.loading = true
    }
    if (event instanceof NavigationEnd) {
      this.loading = false
    }

    // Set loading state to false in both of the below events to hide the spinner in case a request fails
    if (event instanceof NavigationCancel) {
      this.loading = false
    }
    if (event instanceof NavigationError) {
      this.loading = false
    }
  }
}

app.component.html - kök görünümünüz

<div class="loading-overlay" *ngIf="loading">
    <!-- show something fancy here, here with Angular 2 Material's loading bar or circle -->
    <md-progress-bar mode="indeterminate"></md-progress-bar>
</div>

Performans İyileştirilmiş Cevap : Performansı önemsiyorsanız, daha iyi bir yöntem var, uygulamak biraz daha zahmetli ama performans iyileştirmesi fazladan çalışmaya değer. Bunun yerine kullanmanın *ngIfşartlı değer değişimi göstermek için, biz Açısal en kaldıraç olabilir NgZoneve Rendereraçısal değişim tespiti baypas Spinner'a durumunu değiştirecek spinner kapalı / açmak için. I kullanılmasına kıyasla animasyon daha yumuşak olması için bu bulundu *ngIfya da asyncboru.

Bu, bazı ince ayarlarla önceki cevabıma benzer:

app.component.ts - kök bileşeniniz

...
import {
  Router,
  // import as RouterEvent to avoid confusion with the DOM Event
  Event as RouterEvent,
  NavigationStart,
  NavigationEnd,
  NavigationCancel,
  NavigationError
} from '@angular/router'
import {NgZone, Renderer, ElementRef, ViewChild} from '@angular/core'


@Component({})
export class AppComponent {

  // Instead of holding a boolean value for whether the spinner
  // should show or not, we store a reference to the spinner element,
  // see template snippet below this script
  @ViewChild('spinnerElement')
  spinnerElement: ElementRef

  constructor(private router: Router,
              private ngZone: NgZone,
              private renderer: Renderer) {
    router.events.subscribe(this._navigationInterceptor)
  }

  // Shows and hides the loading spinner during RouterEvent changes
  private _navigationInterceptor(event: RouterEvent): void {
    if (event instanceof NavigationStart) {
      // We wanna run this function outside of Angular's zone to
      // bypass change detection
      this.ngZone.runOutsideAngular(() => {
        // For simplicity we are going to turn opacity on / off
        // you could add/remove a class for more advanced styling
        // and enter/leave animation of the spinner
        this.renderer.setElementStyle(
          this.spinnerElement.nativeElement,
          'opacity',
          '1'
        )
      })
    }
    if (event instanceof NavigationEnd) {
      this._hideSpinner()
    }
    // Set loading state to false in both of the below events to
    // hide the spinner in case a request fails
    if (event instanceof NavigationCancel) {
      this._hideSpinner()
    }
    if (event instanceof NavigationError) {
      this._hideSpinner()
    }
  }

  private _hideSpinner(): void {
    // We wanna run this function outside of Angular's zone to
    // bypass change detection,
    this.ngZone.runOutsideAngular(() => {
      // For simplicity we are going to turn opacity on / off
      // you could add/remove a class for more advanced styling
      // and enter/leave animation of the spinner
      this.renderer.setElementStyle(
        this.spinnerElement.nativeElement,
        'opacity',
        '0'
      )
    })
  }
}

app.component.html - kök görünümünüz

<div class="loading-overlay" #spinnerElement style="opacity: 0;">
    <!-- md-spinner is short for <md-progress-circle mode="indeterminate"></md-progress-circle> -->
    <md-spinner></md-spinner>
</div>

1
Harika, yaklaşımınızı takdir ediyorum. Daha önce bana vurdu ve ben dönmek zorunda daha ben üzerindeki kontrolü kaybetti çünkü ben, bu çekici fikri ile benim uzun bir metodu yerini router navigationve it triggering the spinnerardından bunu durdurmak mümkün değil. navigationInterceptorbir çözüm gibi görünüyor ama kırılmayı bekleyen başka bir şey olup olmadığından emin değilim. async requestsOnunla karıştırılırsa sorunu yeniden ortaya çıkaracağını düşünüyorum.
Ankit Singh

1
Belki bu bir noktada işe yaradı? Angular 2.4.8 ile artık çalışmıyor. Sayfa eşzamanlıdır. Döndürücü, tüm sayfa / ana bileşen işlenene ve NavigationEnd'de olana kadar işlenmez. Bu,
iplikçinin

1
Opaklık kullanmak harika bir seçim değildir. Görsel olarak sorun değil, ancak Chrome'da, yükleme resmi / simgesi bir düğmenin veya metnin üstündeyse, düğmeye erişemezsiniz. Ben kullanmayı değişti displayya noneya inline.
im1dermike

2
Bu yaklaşımı beğendim, iyi iş adamım! Ancak bir sorunum var ve nedenini anlamıyorum, NavigasyonEnd'de animasyonu değiştirmezsem, döndürücünün yüklenmesini görebiliyorum, ancak yanlışa geçersem rotalar o kadar hızlı değişiyor ki herhangi bir animasyonu bile göremiyorum :( Denedim hatta ağ yönlendirmesi ile bağlantıyı yavaşlatmak için hala aynı kalıyor :( hiç yükleme yok. Bu konuda bana herhangi bir öneri verebilir misiniz lütfen. Yükleme öğesinde sınıf ekleyip kaldırarak animasyonları kontrol ediyorum. Teşekkürler
d123546

1
Kodunuzu kopyaladığımda bu hatayı aldım 'md-spinner' is not a known element:. Angular'da oldukça yeniyim. Lütfen bana hatanın ne olabileceğini söyler misin?
Manu Chadha

40

GÜNCELLEME: 3 Yeni Yönlendiriciye yükseltme yaptığıma göre, @borislemke'nin yaklaşımı, CanDeactivatekoruma kullanırsanız işe yaramayacak . Eski yöntemime alçalıyorum, ie:bu cevap

Update2 : Yeni-yönlendirici bir görünüm vaat ve Router olaylar cevap tarafından @borislemke spinner uygulamasının ana yönünü kapsayacak şekilde görünüyor, bunu test havent't ama ben bunu tavsiye.

UPDATE1: Bu cevabı şu çağda yazdım :Old-Routerroute-changed bildirilen tek bir olayrouter.subscribe() . Ayrıca aşağıdaki yaklaşımın aşırı yüklendiğini hissettim ve sadece kullanarak yapmaya çalıştım router.subscribe()ve geri tepti çünkü tespit etmenin bir yolu yoktu canceled navigation. Bu yüzden uzun yaklaşıma geri dönmek zorunda kaldım (çift çalışma).


Angular2'de yolunuzu biliyorsanız, ihtiyacınız olan şey budur


Boot.ts

import {bootstrap} from '@angular/platform-browser-dynamic';
import {MyApp} from 'path/to/MyApp-Component';
import { SpinnerService} from 'path/to/spinner-service';

bootstrap(MyApp, [SpinnerService]);

Kök Bileşeni- (Uygulamam)

import { Component } from '@angular/core';
import { SpinnerComponent} from 'path/to/spinner-component';
@Component({
  selector: 'my-app',
  directives: [SpinnerComponent],
  template: `
     <spinner-component></spinner-component>
     <router-outlet></router-outlet>
   `
})
export class MyApp { }

Spinner-Bileşen (aktif değerini buna göre değiştirmek için Spinner hizmetine abone olacak)

import {Component} from '@angular/core';
import { SpinnerService} from 'path/to/spinner-service';
@Component({
  selector: 'spinner-component',
  'template': '<div *ngIf="active" class="spinner loading"></div>'
})
export class SpinnerComponent {
  public active: boolean;

  public constructor(spinner: SpinnerService) {
    spinner.status.subscribe((status: boolean) => {
      this.active = status;
    });
  }
}

Spinner-Service (bu hizmeti önyükleme)

Değişim durumunda durumu değiştirmek için eğirme bileşeni tarafından abone olunacak bir gözlemlenebilir tanımlayın ve eğiriciyi etkin / pasif hale getirmek için işlev ve işlev tanımlayın.

import {Injectable} from '@angular/core';
import {Subject} from 'rxjs/Subject';
import 'rxjs/add/operator/share';

@Injectable()
export class SpinnerService {
  public status: Subject<boolean> = new Subject();
  private _active: boolean = false;

  public get active(): boolean {
    return this._active;
  }

  public set active(v: boolean) {
    this._active = v;
    this.status.next(v);
  }

  public start(): void {
    this.active = true;
  }

  public stop(): void {
    this.active = false;
  }
}

Diğer Tüm Rotaların Bileşenleri

(örneklem):

import { Component} from '@angular/core';
import { SpinnerService} from 'path/to/spinner-service';
@Component({
   template: `<div *ngIf="!spinner.active" id="container">Nothing is Loading Now</div>`
})
export class SampleComponent {

  constructor(public spinner: SpinnerService){} 

  ngOnInit(){
    this.spinner.stop(); // or do it on some other event eg: when xmlhttp request completes loading data for the component
  }

  ngOnDestroy(){
    this.spinner.start();
  }
}

buna da bakın . Animasyonun varsayılan olarak ne kadar açısal desteklediğini bilmiyorum.
Ankit Singh

iplikçi için hizmet yazmama yardım eder misin? Animasyonları ve her şeyi CSS'de kendim yapabilirim, ancak hizmet konusunda bana yardımcı olursanız iyi olur.
Lucas

bu uygulamayı da görün, aynı ama tam olarak değil
Ankit Singh

1
spinner-serviceŞimdilik bazı kodlar ekledim , çalışmasını sağlamak için sadece diğer bölümlere ihtiyacınız var. Ve unutmayınangular2-rc-1
Ankit Singh

1
aslında, bu harika ve işe yarıyor, test amaçlı ipucum ngOnInit'te setTimeout (() => this.spinner.stop (), 5000) ile döndürücünün durmasını geciktirebilirsiniz
Jhonatas Kleinkauff

10

Neden sadece basit css kullanmıyorsunuz:

<router-outlet></router-outlet>
<div class="loading"></div>

Ve tarzınızda:

div.loading{
    height: 100px;
    background-color: red;
    display: none;
}
router-outlet + div.loading{
    display: block;
}

Veya bunu ilk cevap için bile yapabiliriz:

<router-outlet></router-outlet>
<spinner-component></spinner-component>

Ve sonra sadece

spinner-component{
   display:none;
}
router-outlet + spinner-component{
    display: block;
}

Buradaki hile, yeni rotalar ve bileşenlerin her zaman yönlendirici çıkışından sonra görüneceğidir , bu nedenle basit bir css seçici ile yüklemeyi gösterebilir ve gizleyebiliriz.


<router-outlet>, değeri bileşenden üst bileşene geçirmemize izin vermez, bu nedenle yükleyen div'i gizlemek biraz karmaşık olacaktır.
Praveen Rana

1
Ayrıca, uygulamanızın her seferinde anında döndürücüyü görmek için çok sayıda rota değişikliği varsa, çok can sıkıcı olabilir. RxJ'leri kullanmak ve iptal edilmiş bir zamanlayıcı ayarlamak daha iyidir, böylece yalnızca kısa bir gecikmeden sonra görünür.
Simon_Weaver

2

İlk rota için gerekli özel mantığınız varsa, yalnızca aşağıdakileri yapabilirsiniz:

AppComponent

    loaded = false;

    constructor(private router: Router....) {
       router.events.pipe(filter(e => e instanceof NavigationEnd), take(1))
                    .subscribe((e) => {
                       this.loaded = true;
                       alert('loaded - this fires only once');
                   });

Buna, sayfanın üst kısmında görünen sayfa alt bilgimi gizlemek için bir ihtiyacım vardı. Ayrıca, yalnızca ilk sayfa için bir yükleyici istiyorsanız, bunu kullanabilirsiniz.


0

Bu mevcut çözümü de kullanabilirsiniz . Demo burada . Youtube yükleme çubuğuna benziyor. Yeni buldum ve kendi projeme ekledim.


çözücülere yardımcı olur mu? Benim sorunum, çözücüler verileri geri getirirken, spinner'ı hiçbir yerde gösteremiyorum çünkü asıl hedef bileşen ngOninit henüz çağrılmadı !! Benim fikrim spinner'ı ngOnInit'te göstermek ve çözülen veriler rota aboneliğinden döndüğünde onu gizlemekti
kuldeep
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.