Показати екран завантаження під час навігації між маршрутами в куті 2


Відповіді:


196

Поточний кутовий маршрутизатор забезпечує події навігації. Ви можете підписатися на них та внести зміни в інтерфейс відповідно. Не забудьте рахувати в інших подіях, таких як NavigationCancelі NavigationErrorзупинити ваш спінер, якщо переходи маршрутизатора не вдаються.

app.component.ts - ваш кореневий компонент

...
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 - ваш кореневий вигляд

<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>

Відповідь на покращення продуктивності : Якщо ви піклуєтесь про ефективність, існує кращий метод, реалізовувати дещо складніше, але підвищення продуктивності буде вартим додаткової роботи. Замість того, *ngIfщоб умовно показувати спінер, ми могли б скористатись Angular NgZoneі Rendererвключити / вимкнути спінер, що обійде виявлення змін Angular, коли ми змінимо стан спінера. Я знайшов це, щоб зробити анімацію більш гладкою порівняно з використанням *ngIfабо anasync трубою.

Це схоже на мою попередню відповідь з деякими налаштуваннями:

app.component.ts - ваш кореневий компонент

...
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 - ваш кореневий вигляд

<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
Чудово, я ціную ваш підхід. Він вдарив мене , перш ніж я замінив мій довгий метод з цієї привабливої ідеєю, ніж я повинен був повернутися тому що я втратив контроль над router navigationі , it triggering the spinnerа потім не в змозі зупинити його. navigationInterceptorздається, що це рішення, але я не впевнений, чи чекає щось інше. Якщо async requestsце буде змішано з цим, це знову введе проблему, я думаю.
Анкіт Сінгх

1
Може, це спрацювало в якийсь момент? Зараз це не працює з кутовим 2.4.8. Сторінка є синхронною. Spinner не відображається, поки не відобразиться вся сторінка / батьківський компонент, і це не буде в NavigationEnd. Це перемагає точку
спінера

1
Використання непрозорості не є чудовим вибором. Візуально це нормально, але в Chrome, якщо завантажувальне зображення / значок знаходиться поверх кнопки чи тексту, ви не можете отримати доступ до кнопки. Я змінив його використовувати displayабо noneчи inline.
im1dermike

2
Мені подобається такий підхід, хороша людина! Але у мене є проблема, і я не розумію, чому, якщо я не перемикаю анімацію на NavigationEnd, я бачу завантаження спінера, але якщо я переключаюсь на помилкове, маршрути змінюються так швидко, що я не можу навіть бачити будь-яку анімацію :( Я спробував з рівномірною мережевою передачею, щоб уповільнити з'єднання, але його все одно залишається тим самим :( взагалі немає завантаження. Чи можете ви дати мені будь-які пропозиції щодо цього, будь ласка, я
керую

1
Я отримав цю помилку, коли скопіював ваш код 'md-spinner' is not a known element:. Я зовсім новачок у Angular. Скажіть, будь ласка, в чому може бути помилка?
Ману Чадха

40

ОНОВЛЕННЯ: 3 Тепер, коли я перейшов на новий маршрутизатор, підхід @borislemke не буде працювати, якщо ви використовуєте CanDeactivateохорону. Я принижую свій старий метод,ie: це відповідь

ОНОВЛЕННЯ2 : Події маршрутизатора в новому маршрутизаторі виглядають багатообіцяючим, а відповідь - @borislemke , здається, охоплює основний аспект реалізації вращателя я havent't тестував , але я рекомендую його.

ОНОВЛЕННЯ1: Цю відповідь я написав ще в епохуOld-Router , коли раніше було тільки одна подія route-changedповідомлений через router.subscribe(). Я також відчував перевантаженість нижчезазначеного підходу і намагався це зробити лише за допомогою router.subscribe(), і він дав зворотну реакцію, тому що не було можливості виявити canceled navigation. Тому мені довелося повернутися до тривалого підходу (подвійна робота).


Якщо ви знаєте, як подорожувати в Angular2, це те, що вам знадобиться


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]);

Root Component- (MyApp)

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, щоб відповідно змінити значення активного)

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 (завантажте цю послугу)

Визначте спостережуваний, який повинен підписати спінер-компонент, щоб змінити стан при зміні, а також функцію знати та встановлювати спінер на активний / неактивний.

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;
  }
}

Усі інші компоненти маршрутів

(зразок):

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();
  }
}

дивіться це також. Я не знаю, наскільки Animation підтримує кутову підтримку за замовчуванням.
Анкіт Сінгх

чи можете ви допомогти мені написати послугу для спінера? Я можу робити анімації та інше в CSS, але було б добре, якби ви могли мені допомогти в сервісі.
Лукас

дивіться також цю реалізацію , таку ж, але не зовсім точно
Анкіт Сінгх

1
Я додав код spinner-serviceзараз, вам просто потрібно в інші частини, щоб він працював. І пам’ятайте, що цеangular2-rc-1
Анкіт Сінгх

1
насправді це приголомшливо і працює, моя підказка для тестування ви можете затримати зупинку спінера з setTimeout (() => this.spinner.stop (), 5000) у ngOnInit
Jhonatas Kleinkauff

10

Чому б не просто використовувати простий css:

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

І у ваших стилях:

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

Або навіть ми можемо це зробити для першої відповіді:

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

І то просто просто

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

У цьому фокус полягає в тому, що нові маршрути та компоненти завжди з’являться після маршрутизатора , тому за допомогою простого селектора css ми можемо показати та приховати завантаження.


<router-outlet> не дозволяє нам передавати значення від компонента до батьківського компонента, тому приховати діва завантаження буде трохи складніше.
Правен Рана

1
Крім того, це може бути дуже прикро, якщо у вашій програмі багато змін маршруту, щоб миттєво побачити спінер. Краще використовувати RxJs і встановити розблокований таймер, щоб він з'явився лише після невеликої затримки.
Simon_Weaver

2

Якщо у вас є спеціальна логіка, необхідна лише для першого маршруту, ви можете зробити наступне:

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');
                   });

У мене була потреба в цьому, щоб приховати нижній колонтитул сторінки, який інакше з’являвся вгорі сторінки. Також якщо ви хочете завантажувати лише першу сторінку, ви можете скористатися цією.


0

Ви також можете використовувати це існуюче рішення . Демо тут . Це схоже на панель завантаження YouTube. Я просто знайшов це і додав до власного проекту.


чи допомагає це з розв’язувачами? Моя проблема полягає в тому, що, незважаючи на те, що розв'язувачі повертають дані, я не в змозі показувати спінер на будь-якому місці, оскільки фактичний цільовий компонент ngOninit ще не був названий !! Моя ідея полягала в тому, щоб показати вертушку в ngOnInit і приховати його коли - то вирішено дані повертаються з підписки маршруту
Kuldeep
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.