Индикатор активности Nativescript неправильно прослушивает busy = переменную

#angular #nativescript

#angular #nativescript

Вопрос:

я создаю небольшое приложение, в котором я хочу иметь ActivityIndicator всякий раз, когда запрос отправлялся на сервер.

Итак, в моей службе пользовательского интерфейса у меня есть тема, и я слушаю ее в моем ActivityIndicatorComponent. Результат: он начинает быть «занят», но не останавливается, даже если переменная в атрибуте индикатора «busy» имеет значение false.

Значит, Idndicator каким-то образом «не обновляется» после однократной инициализации?

Мой пользовательский сервис

     @Injectable({ providedIn: 'root'})
export class UiService {
    private _drawerState=  new BehaviorSubject<void>(null);
    isLoading = new Subject<boolean>();

    get drawerState() {
        return this._drawerState.asObservable();
    }

    toggleDrawer() {
        this._drawerState.next(null);
    }

    setIsLoading(value: boolean) {
        this.isLoading.next(value);
    }

}
  

Моя служба сокетов, которая вызывает метод в службе пользовательского интерфейса:

 export class SocketioService {
socket;

currentRoom: ChatRoom;
message_receiver = new Subject<Message>();
room_receiver = new Subject<ChatRoom>();


constructor(private router: RouterExtensions, private ui: UiService) {
    this.socket = SocketIO.connect("http://localhost:3000");
    console.log("SOCKET: "   this.socket);

    this.socket.on("chat room", (room) => this.onReceivingChatRoom(room));

}


onRequestRoomlist(data) {
    this.ui.setIsLoading(true);
    this.socket.emit("request rooms", data);
}

onReceivingChatRoom(room) {
    this.ui.setIsLoading(false);
    console.log("Received Chat Room");
    this.room_receiver.next(new ChatRoom(room.name, room.description, room.image, room.protection));
}
}
  

И, наконец, я слушаю тему в моем ActivityIndicatorComponent:

 export class ActivityIndicatorComponent implements OnInit {

  isLoading: boolean;

  constructor(private ui: UiService) { }

  ngOnInit() {
    this.ui.isLoading.subscribe(
        (value: boolean) => {
          this.isLoading = value;
        }
    )
  }

}
  

Вот XML-файл ActivityIndicatorComponent

 <ActivityIndicator #indicator row="1" col="1" width="50" height="50" busy="{{isLoading}}" color="#6495ed"></ActivityIndicator>
  

Комментарии:

1. Можете ли вы поделиться примером игровой площадки, где проблема может быть воспроизведена?

Ответ №1:

Вероятно, сокет работает вне зоны Angular, поэтому при изменении переменной он отправляет обновление вашему объекту и, поскольку он находится вне зоны, не запускает обнаружение изменений.

Это можно исправить двумя способами:

  1. Как вы уже исправили, вручную вызвав detectChanges()
  2. При запуске внутри angular zone с ngZone.run(() => {/* code */});

Вы также можете использовать этот наблюдаемый оператор, чтобы гарантировать, что код выполняется внутри зоны.

 import { NgZone } from '@angular/core';
import { Observable } from 'rxjs/Observable';

export function enterZone(zone: NgZone) {
  return <T>(source: Observable<T>) =>
    new Observable<T>(observer =>
      source.subscribe({
        next: (x) => zone.run(() => observer.next(x)),
        error: (err) => observer.error(err),
        complete: () => observer.complete()
    })
   );
}
  

Поэтому вы можете использовать: this.ui.isLoading.pipe(enterZone(ngZone)).subscribe() . Или, что еще лучше, вы можете определить в своем сервисе:

isLoading$ = this.isLoading.pipe(enterZone(ngZone))

Итак, вы уверены, что все, на что подписывается isLoading$ , на самом деле выполняется внутри зоны Angular.

Редактировать:

Пример пользовательского сервиса с наблюдаемым:

 @Injectable({ providedIn: 'root'})
export class UiService {
    private _drawerState=  new BehaviorSubject<void>(null);
    isLoading = new Subject<boolean>();
    isLoading$ = this.isLoading.pipe(enterZone(this.ngZone));

    get drawerState() {
        return this._drawerState.asObservable();
    }

    constructor(private ngZone: NgZone) {}

    toggleDrawer() {
        this._drawerState.next(null);
    }

    setIsLoading(value: boolean) {
        this.isLoading.next(value);
    }

}
  

Комментарии:

1. Спасибо! Это выглядит потрясающе. Не могли бы вы показать мне мой пользовательский сервис с вашим примером кода, чтобы это: isLoading$ = this.isLoading.pipe(enterZone(ngZone)) работало? Я не смог заставить его работать.

2. Я обновил ответ с помощью примера. Все, что вам нужно сделать, это подписаться на isLoading$ вместо isLoading (вы даже можете сделать isLoading приватным)

Ответ №2:

Я исправил это, используя ChangeDetectorRef в ActivityIndicatorComponent.

После получения нового значения для подписки я позвонил changeRef.detectChanges(); , и это работает

Комментарии:

1. Вам не нужно вызывать детектор изменений или зону внутри подписчика. Тем не менее, реальная проблема может заключаться в чем-то другом, чего вам не хватает в вашем сервисе или около того.