AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 78511524
Accepted
Razvan Zamfir
Razvan Zamfir
Asked: 2024-05-21 19:23:37 +0800 CST2024-05-21 19:23:37 +0800 CST 2024-05-21 19:23:37 +0800 CST

Como isolar uma chamada de API para um único componente neste aplicativo Angular 16?

  • 772

Tenho trabalhado em um SPA com Angular 16, TypeScript e The Movie Database (TMDB).

Encontrei um problema após o desenvolvimento de um recurso de "rolagem infinita".

No MoviesByGenrecomponente eu tenho:

import { Component } from '@angular/core';
import { GenreResponse, Genre } from '../../models/Genre';
import { MovieResponse, Movie } from '../../models/Movie';
import { MovieService } from '../../services/movie-service.service';
import { ActivatedRoute } from '@angular/router';
import { distinctUntilChanged, fromEvent, map, startWith } from 'rxjs';

@Component({
  selector: 'app-movies-by-genre',
  templateUrl: './movies-by-genre.component.html',
  styleUrls: ['./movies-by-genre.component.scss']
})

export class MoviesByGenre {
  constructor(
    private activatedRoute: ActivatedRoute,
    private movieService: MovieService
  ) { }

  public genreName: string | undefined = '';
  public movieResponse!: MovieResponse;
  public movies: Movie[] = [];
  public genreResponse!: GenreResponse;
  public genres: Genre[] | undefined = [];
  public genreId!: number;
  public maxPage: number = 10;
  public pageNumber: number = 1;
  public isLoading: boolean = false;

  public getMoviesByGenre(): void {
    // Get genre id (from URL parameter)
    this.genreId = Number(this.activatedRoute.snapshot.paramMap.get('id'));

    // Get genre name from genres array
    this.movieService.getAllMovieGenres().subscribe((response) => {
      this.genreResponse = response;
      this.genres = this.genreResponse.genres;

      if (this.genres && this.genres.length) {
        let currentGenre = this.genres.find(
          (genre) => genre.id === this.genreId
        );
        if (currentGenre) {
          this.genreName = currentGenre.name || '';
          this.movieService.defaultTitle = this.genreName;
        }
      }
    });

    this.loadMoreMovies(this.genreId, this.pageNumber);
  }

  public loadMoreMovies(genreId: number, pageNumber: number) {
    // Get movies by genre id
    this.movieService
      .getMoviesByGenre(genreId, pageNumber)
      .subscribe((response) => {
        this.movieResponse = response;
        this.movies.push(...(this.movieResponse?.results || []));
      });
  }

  ngAfterViewInit() {
    fromEvent(window, 'scroll')
      .pipe(
        startWith(0),
        map(() => window?.scrollY),
        distinctUntilChanged()
      )
      .subscribe((scrollPos: any) => {
        if (!this.movies?.length) {
          return;
        }
        if (
          Math.round(scrollPos + window.innerHeight) >=
          document.documentElement.scrollHeight &&
          this.pageNumber < this.maxPage
        ) {
          this.pageNumber++;
          this.loadMoreMovies(this.genreId, this.pageNumber);
        }
      });
  }

  ngOnInit() {
    this.activatedRoute.params.subscribe(() => {
      this.movies = [];
      this.getMoviesByGenre();
    });
  }

  ngOnDestroy() {
    this.movieService.defaultTitle = '';
  }
}

No MovieServiceserviço:

import { environment } from '../../environments/environment';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { MovieResponse, Movie } from '../models/Movie';
import { GenreResponse } from '../models/Genre';
import { TrailerResponse } from '../models/Trailer';

@Injectable({
  providedIn: 'root'
})

export class MovieService {

  constructor(private http: HttpClient) { }

  public defaultTitle: string = '';


  public getAllMovieGenres(): Observable<GenreResponse> {
    return this.http.get<GenreResponse>(`${environment.apiUrl}/genre/movie/list?api_key=${environment.apiKey}`);
  }

  public getMoviesByGenre(id: Number, pageNumber: Number): Observable<MovieResponse> {
    return this.http.get<MovieResponse>(`${environment.apiUrl}/discover/movie?api_key=${environment.apiKey}&with_genres=${id}&page=${pageNumber}`);
  }

  public getMovieDetails(id: Number): Observable<Movie>{
    return this.http.get<Movie>(`${environment.apiUrl}/movie/${id}?api_key=${environment.apiKey}`);
  }
}

A cada rolagem até o final da página do by-genre/:idpercurso, são carregados mais 20 filmes, com limite definido em 10 páginas (200 filmes).

O problema

O problema é que mesmo em outras rotas, como a rota de detalhes do filme, uma chamada de API é feita ao rolar até o final da página e mostra o botão giratório de carregamento, para o qual usei uma técnica de interceptador http , desnecessariamente.

O objetivo

O objetivo é tornar a rolagem infinita específica para o MoviesByGenrecomponente:

Para isso, adicionei um booleano ao MoviesByGenrecomponente e utilizei-o da seguinte forma:

public isLoadMore: boolean = true;

if (this.isLoadMore) {
  this.loadMoreMovies(this.genreId, this.pageNumber);
} 

Mas as solicitações ainda são feitas em outros componentes ao rolar até o final da página.

Parece que falhei em identificar a causa do problema e em fornecer uma solução viável .

Questões

  1. O que causa esse problema?
  2. Qual é a maneira mais confiável de consertar isso?
javascript
  • 1 1 respostas
  • 43 Views

1 respostas

  • Voted
  1. Best Answer
    Naren Murali
    2024-05-21T19:41:48+08:002024-05-21T19:41:48+08:00

    Temos que unsubscriberecorrer às assinaturas ativas para resolver esse problema, quando o componente é destruído, isso não significa necessariamente que as assinaturas foram destruídas, temos que addligar todas elas subscriptione chamar o unsubscribemétodo on ngOnDestroydo componente para eliminar todas as assinaturas!

    É uma boa prática adicionar todas as assinaturas a uma propriedade de assinatura e, em seguida, cancelá-las completamente!

    import { Component } from '@angular/core';
    import { GenreResponse, Genre } from '../../models/Genre';
    import { MovieResponse, Movie } from '../../models/Movie';
    import { MovieService } from '../../services/movie-service.service';
    import { ActivatedRoute } from '@angular/router';
    import {
      Subscription,
      distinctUntilChanged,
      fromEvent,
      map,
      startWith,
    } from 'rxjs';
    
    @Component({
      selector: 'app-movies-by-genre',
      templateUrl: './movies-by-genre.component.html',
      styleUrls: ['./movies-by-genre.component.scss'],
    })
    export class MoviesByGenre {
      private subscription = new Subscription(); // <- changed here!
      constructor(
        private activatedRoute: ActivatedRoute,
        private movieService: MovieService
      ) {}
    
      public genreName: string | undefined = '';
      public movieResponse!: MovieResponse;
      public movies: Movie[] = [];
      public genreResponse!: GenreResponse;
      public genres: Genre[] | undefined = [];
      public genreId!: number;
      public maxPage: number = 10;
      public pageNumber: number = 1;
      public isLoading: boolean = false;
    
      public getMoviesByGenre(): void {
        // Get genre id (from URL parameter)
        this.genreId = Number(this.activatedRoute.snapshot.paramMap.get('id'));
    
        // Get genre name from genres array
        this.subscription.add( // <- changed here!
          this.movieService.getAllMovieGenres().subscribe((response) => {
            this.genreResponse = response;
            this.genres = this.genreResponse.genres;
    
            if (this.genres && this.genres.length) {
              let currentGenre = this.genres.find(
                (genre) => genre.id === this.genreId
              );
              if (currentGenre) {
                this.genreName = currentGenre.name || '';
                this.movieService.defaultTitle = this.genreName;
              }
            }
          })
        );
    
        this.loadMoreMovies(this.genreId, this.pageNumber);
      }
    
      public loadMoreMovies(genreId: number, pageNumber: number) {
        // Get movies by genre id
        this.subscription.add( // <- changed here!
          this.movieService
            .getMoviesByGenre(genreId, pageNumber)
            .subscribe((response) => {
              this.movieResponse = response;
              this.movies.push(...(this.movieResponse?.results || []));
            })
        );
      }
    
      ngAfterViewInit() {
        this.subscription.add( // <- changed here!
          fromEvent(window, 'scroll')
            .pipe(
              startWith(0),
              map(() => window?.scrollY),
              distinctUntilChanged()
            )
            .subscribe((scrollPos: any) => {
              if (!this.movies?.length) {
                return;
              }
              if (
                Math.round(scrollPos + window.innerHeight) >=
                  document.documentElement.scrollHeight &&
                this.pageNumber < this.maxPage
              ) {
                this.pageNumber++;
                this.loadMoreMovies(this.genreId, this.pageNumber);
              }
            })
        );
      }
    
      ngOnInit() {
        this.subscription.add( // <- changed here!
          this.activatedRoute.params.subscribe(() => {
            this.movies = [];
            this.getMoviesByGenre();
          })
        );
      }
    
      ngOnDestroy() {
        this.subscription.unsubscribe(); // <- changed here!
        this.movieService.defaultTitle = '';
      }
    }
    
    • 1

relate perguntas

  • classificação de mesclagem não está funcionando - código Javascript: não é possível encontrar o erro mesmo após a depuração

  • método select.remove() funciona estranho [fechado]

  • Sempre um 401 res em useOpenWeather () - react-open-weather lib [duplicado]

  • O elemento de entrada não possui atributo somente leitura, mas os campos ainda não podem ser editados [fechado]

  • Como editar o raio do primeiro nó de um RadialTree D3.js?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve