#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, поэтому при изменении переменной он отправляет обновление вашему объекту и, поскольку он находится вне зоны, не запускает обнаружение изменений.
Это можно исправить двумя способами:
- Как вы уже исправили, вручную вызвав
detectChanges()
- При запуске внутри 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. Вам не нужно вызывать детектор изменений или зону внутри подписчика. Тем не менее, реальная проблема может заключаться в чем-то другом, чего вам не хватает в вашем сервисе или около того.