web-dev-qa-db-fra.com

Angular2 / 4: Actualiser les données en temps réel

Je dois actualiser les données d'une page de composant dans un intervalle. De plus, j'ai besoin d'actualiser les données après une action. J'utilise Obeservables dans le service pour pouvoir m'abonner lorsque la réponse est prête. Je pousse les abonnements vers un objet afin de pouvoir clarifier que le ngDestroy, je pense disposer des méthodes suivantes pour atteindre le même résultat.

Méthode 1: setInterval

J'ai défini un intervalle sur ngOnInit, qui appellera refreshData à intervalle égal. L'objet intervalle sera effacé à l'aide de la méthode clearInterval dans la méthode ngOnDestroy.

export class MyComponent implements OnInit, OnDestroy {
    private subscription: Subscription = new Subscription();

    data: any;
    interval: any;

    ngOnInit() {
        this.refreshData();
        this.interval = setInterval(() => { 
            this.refreshData(); 
        }, 5000);
    }

    ngOnDestroy() {
        this.subscription.unsubscribe();
        clearInterval(this.interval);
    }

    refreshData(){
        this.subscription.add(
            this.myService.getData()
                .subscribe(data => {
                    this.data = data;
                })
        );
    }

    doAction(){
        this.subscription.add(
            this.myService.doAction()
                .subscribe(result => {
                    if(result === true){
                        this.refreshData();
                    }
                })
        );
    }
}

Q1: À chaque appel d'actualisation, un abonnement sera ajouté à l'objet subscription, cela augmentera-t-il l'utilisation de la mémoire et risque-t-il de bloquer le navigateur si l'utilisateur garde la page ouverte pendant un certain temps?

Méthode 2: Observable.timer

Cette méthode utilise un minuteur qui sera créé après l'actualisation des données.

export class MyComponent implements OnInit, OnDestroy {
    private subscription: Subscription = new Subscription();

    data: any;

    ngOnInit() {
        this.refreshData();
    }

    ngOnDestroy() {
        this.subscription.unsubscribe();
    }

    refreshData(){
        this.subscription.add(
            this.myService.getData()
                .subscribe(data => {
                    this.data = data;
                    this.subscribeToData();
                })
        );
    }

    subscribeToData(){
        this.subscription.add(
            Observable.timer(10000).subscribe(() => this.refreshData())
        );
    }

    doAction(){
        this.subscription.add(
            this.myService.doAction()
                .subscribe(result => {
                    if(result === true){
                        this.refreshData();
                    }
                })
        );
    }
}

Q2: J'ai la même question (Q1) ici. De cette façon, les minuteries seront également ajoutées à l'objet d'abonnement. Ainsi, les abonnements de l'objet d'abonnement sont doublés.

Q3: Pour actualiser les données après la méthode d'action - doAction (), le paramètre refreshData est appelé. Cela créera-t-il une autre chaîne de minuterie?

Q4: Quelle est la meilleure solution sans fuites de mémoire ou s’il existe une autre solution?

15
Jithin

Vous devriez pouvoir le faire sans problèmes:

ngOnInit() {
    this.refreshData();
    this.interval = setInterval(() => { 
        this.refreshData(); 
    }, 5000);
}

refreshData(){
    this.myService.getData()
        .subscribe(data => {
            this.data = data;
        })
    );
}

Selon cet article Angular s'occupera du nettoyage après lui-même.

Cependant , si vous envisagez d'avoir un flux de données en direct dans votre application, je vous suggère de modifier votre composant afin que, plutôt que de vous abonner à chaque réponse de Dans la requête http de votre service, vous vous abonnez une fois à une nouvelle propriété observable data$ de votre service dans la ngOnInit() de votre composant. Ensuite, à intervalle (comme vous le faites), appelez updateData() sur votre service (ou configurez l'intervalle à l'intérieur votre service), mais ne vous abonnez pas. Lorsque votre service récupère les données, il transmet la valeur suivante à sa propriété observable data$, Vous offrant ainsi un flux de données de votre service auquel vous pouvez réagir n'importe où dans votre application.

ngOnInit() {
    this.myService.data$.subscribe(data => { // subscribe once to the data stream
        this.data = data;
    })

    this.refreshData();
    this.interval = setInterval(() => { 
        this.refreshData(); 
    }, 5000);
}

refreshData(){
    this.myService.updateData(); // simply signal for the service to update its data stream
}

Avec myService.data$ Étant un BehaviourSubject observable mis à jour dans votre service, quelque chose comme ceci:

public data$: BehaviorSubject<any> = new BehaviorSubject({});

updateData() {
    let data = this.http.get('http://www.data.com').map((data)=>{
        return data.json();
    }).do((data)=>{
        this.data$.next(data);
    })
}

De cette façon, vous pouvez éviter les abonnements multiples et rendre le flux de données disponible pour tout composant qui en a besoin.

21
SpaceFozzy

J'étends juste un peu la réponse de @ SpaceFozzy afin qu'elle soit utile aux futurs visiteurs ici. Pour actualiser les données, j'ai utilisé la méthode de @SpaceFozzy. Mais pour les abonnements, j'ai eu une meilleure approche. S'il vous plaît jeter un oeil à la réponse - https://stackoverflow.com/a/4117716 . J'ai donc mis à jour mon service et composant comme suit. J'espère que cela t'aides.

Mon composant

import { Subject } from 'rxjs/Subject';

export class MyComponent implements OnInit, OnDestroy {
    private unsubscribe: Subject = new Subject();

    data: any;
    interval: any;

    ngOnInit() {
        this.refreshData();
        if(this.interval){
            clearInterval(this.interval);
        }
        this.interval = setInterval(() => {
            this.refreshData();
        }, 10000);


        this.myService.data$.takeUntil(this.unsubscribe)
            .subscribe(data => {
                this.data = data;
            });

    }

    ngOnDestroy() {
        this.unsubscribe.next();
        this.unsubscribe.complete();
    }

    refreshData(){
        this.myService.updateData()
            .takeUntil(this.unsubscribe)
            .subscribe();
    }

    doAction(){
        this.subscription.add(
            this.myService.doAction()
                .subscribe(result => {
                    if(result === true){
                        this.refreshData();
                    }
                })
        );
    }
}

Mon service

import { Observable, BehaviorSubject } from 'rxjs';

@Injectable()
export class MyService {
    private dataSubject: BehaviorSubject<YourDataModel[]> = new BehaviorSubject([]);

    data$: Observable<YourDataModel[]> = this.dataSubject.asObservable();

    updateData(): Observable<any>  {
        return this.getData().do((data) => {
            this.dataSubject.next(data);
        });
    }

    // My data is an array of model objects
    getData(): Observable<YourDataModel[]>{
        return this.http.get('/path')
            .map((response: Response) => {
                let data = response.json() && response.json().your_data_objects;
                if(data){
                    return data;
                }
            })
    }

}
4
Jithin