简体   繁体   中英

Observable unsubscribe

I have read that every component should unsubscribe to all subscriptions at the end in the

ngOnDestroy()

for instance. I have the following blog component:

import { Component, OnInit } from '@angular/core';
import { ISubscription } from "rxjs/Subscription";

import { CarouselConfig } from 'ngx-bootstrap/carousel';

import { BlogPost } from '../shared/blogPost';
import { BlogPostFactory } from '../shared/blogPost-factory';

import { DataService } from '../data.service';

@Component({
  selector: 'ca-blog',
  templateUrl: './blog.component.html',
  styleUrls: ['./blog.component.css'],
  providers: [{provide: CarouselConfig, useValue: {interval: 1500, noPause: true}}]
})
export class BlogComponent implements OnInit {
  private blogPosts: BlogPost[] = [];
  private heartsUp: string[] = [];
  private likesUp: string[] = [];
  private heartSub: ISubscription;
  private likeSub: ISubscription;

  constructor(private dataService: DataService) {
    window.onscroll = () => {
      let windowHeight = "innerHeight" in window ? window.innerHeight : document.documentElement.offsetHeight;
      let body = document.body;
      let html = document.documentElement;
      let docHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
      let windowBottom = windowHeight + window.pageYOffset;

      if(windowBottom >= docHeight) {
        let urlSearchParams = { last: this.blogPosts.length };

        this.dataService.getBlogPosts(urlSearchParams).subscribe( result => {
          for(let i=0; i<result.length; i++){
            this.blogPosts.push(BlogPostFactory.fromObject(result[i]));
          }
        });
      }
    };
  }

  ngOnInit() {
    this.dataService.getBlogPosts().subscribe( result => {
      for(let i=0; i<result.length; i++){
        this.blogPosts.push(BlogPostFactory.fromObject(result[i]));
      }
    });
  }

  incrementHearts(index) : void {
    let idIndex : number = this.heartsUp.indexOf(this.blogPosts[index].id);

    if(idIndex != -1){
      this.blogPosts[index].hearts--;

      this.heartsUp.splice(idIndex, 1);

    } else {
      this.blogPosts[index].hearts++;

      this.heartsUp.push(this.blogPosts[index].id);
    }

    this.heartSub = this.dataService.editBlogPost(this.blogPosts[index].id, { hearts: this.blogPosts[index].hearts }).subscribe();
  }

  incrementLikes(index) : void {
    let idIndex : number = this.likesUp.indexOf(this.blogPosts[index].id);

    if(idIndex != -1){
      this.blogPosts[index].likes--;

      this.likesUp.splice(idIndex, 1);

    } else {
      this.blogPosts[index].likes++;

      this.likesUp.push(this.blogPosts[index].id);
    }

    this.likeSub = this.dataService.editBlogPost(this.blogPosts[index].id, { likes: this.blogPosts[index].likes }).subscribe();
  }

  ngOnDestroy() {
//    this.likeSub.unsubscribe();
//    this.heartSub.unsubscribe();
  }
}

But when i uncomment the two lines in the ngOnDestroy() function then my site crashes. It says that heartSub and likeSub is undefined. The appropriate HTML file looks like this:

<div class="container">
  <div *ngFor="let blogPost of blogPosts; let i=index">
    <div *ngIf="i !== 0">
      <hr class="my-5">
    </div>
    <div class="row">
      <div class="col">
        <article>
          <section>
            <header style="" class="mb-4">
              <h1 style="display:inline" class="m-0 p-0">{{ blogPost.title }}</h1><small style="opacity:0.5" class="d-block d-sm-inline ml-sm-3">zuletzt bearbeitet am {{ blogPost.lastEdited | date }} von <a href="#" style="color:black"><strong>{{ blogPost.author.username }}</strong></a></small>
              <p class="mt-1 mt-sm-auto"><i class="fa fa-tags mr-2"></i><a *ngFor="let hashtag of blogPost.hashtags" href="#" class="badge badge-secondary mr-2">#{{ hashtag }}</a></p>
            </header>

            <div class="m-0 p-0" [innerHTML]="blogPost.body">
            </div>
            <div>
              <small class="heartUp"><a (click)="incrementHearts(i)" [ngStyle]="{'color':heartsUp.includes(blogPost.id) ? '#E63946' : 'rgba(0,0,0,0.5)'}"><i class="fa fa-heart mr-1"></i>{{ blogPost.hearts }}</a></small>
              <small class="likeUp"><a (click)="incrementLikes(i)" [ngStyle]="{'color':likesUp.includes(blogPost.id) ? '#3b5998' : 'rgba(0,0,0,0.5)'}"><i class="fa fa-thumbs-up mr-1"></i>{{ blogPost.likes }}</a></small>
              <small class="reply"><a><i class="fa fa-mail-reply mr-1"></i>Reply</a></small>
            </div>
            <div>
              <h2 class="my-3">Kommentare</h2>
              <div class="row no-gutters">
                <div class="col-auto mr-3">
                  <img src="https://semantic-ui.com/images/avatar/small/jenny.jpg" class="img-fluid">
                </div>
                <div class="col">
                  <h5 style="margin:0;padding:0;display:inline">Joe Henderson</h5><small style="display:inline;margin:0;padding:0;opacity:0.5" class="ml-2"> 1 day ago</small>
                  <div style="margin:0;padding:0">The hours, minutes and seconds stand as visible reminders that your effort put them all there.

                  Preserve until your next run, when the watch lets you see how Impermanent your efforts are.</div>
                  <div>and some other text</div>
                  <small style="margin:0;padding:0;opacity:0.5;margin-right:10px"><i class="fa fa-heart mr-1"></i>3</small>
                  <small style="margin:0;padding:0;opacity:0.5;margin-right:10px"><i class="fa fa-thumbs-o-up mr-1"></i>12</small>
                  <small style="margin:0;padding:0;opacity:0.5"><i class="fa fa-mail-reply mr-1"></i>Reply</small>
                </div>
              </div>
            </div>
          </section>
        </article>
      </div>
    </div>
  </div>
</div>

It uses the CSS framework Bootstrap, but this shouldnt matter. So my site loads a lot of posts and every post has a like and a heart button which can be pressed up and down by the user. If it's released then i update my post in the database.

I want to unsubscribe from the subscription at the end, but i cant. I don't know whats the problem. :( Nevertheless, i think the up and down voting is really bad written but how could it be done? Im thankful for any help. :)

Best regards, Sven

I'd go the route of creating a new Subject in your component where you have subscription calls.

private ngUnsubscribe: Subject = new Subject();

Then, in the ngOnDestroy you call

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

and before each subscription you do takeUntil:

    this.likeSub = this.dataService.editBlogPost(this.blogPosts[index].id, { likes: this.blogPosts[index].likes })
.takeUntil(this.ngUnsubscribe)
.subscribe();

This is the method I use in my applications and you can read more about it here:

Angular/RxJs When should I unsubscribe from `Subscription`

I agree with @Filip, I went the same route as he described.

Regarding your current problem. If the functions incrementLikes() and incrementHearts() don't get called then those two subscriptions will definitely be undefined .

Sv En, if you don't execute incrementLikes() or incrementHearts() your variable not hav evalue. You can do

ngOnDestroy() {
    if (this.likeSub)  //if exist
      this.likeSub.unsubscribe();
    if (this.heartSub)  //if exist
    this.heartSub.unsubscribe();
  }

Filip, you needn't use a Subject, just a boolean variable in the way

this.dataService.editBlogPost(..)
   .takeWhile(() => this.isAlive)
   .subscribe(...)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM