Búsqueda de sitios web

javascript - No se puede poner un observable suscrito en una colección como una cadena


Soy un poco nuevo en TypeScript y JavaScript, y me está costando mucho trabajo entender cómo funcionan las colecciones y la entrada/salida de archivos. Estoy tratando de obtener datos de un archivo JSON, lo cual he logrado, aunque cuando los pongo en una colección, la colección no tiene los datos.

Aquí está el código:

En mi clase de servicio:

  private configuration = "../../assets/Configurations/testConfiguration.json";

  constructor(private http: HttpClient) {}

  getBlogs(blog: string): Observable<IBlogPost[]> {
    return this.http.get<IBlogPost[]>(blog);
  }

  getConfigurations() {
    var configurationsData = [];
    this.http.get(this.configuration).subscribe(data => {
      configurationsData.push(data);
      console.log(data);
      // This will work and will print all the paths
    });

    //This will not work and will not print them, like if the collection is empty
    configurationsData.forEach(x => console.log(x));

    return configurationsData;
  }

Dónde obtengo mi clase de servicio inyectada:

blogs: IBlogPost[] = [];

  private blogsPaths: string[] = [];

  errorMessage = "";

  constructor(private appUtilityService: AppUtilityServices) {}

  ngOnInit() {
    //This will not work, blogsPaths will not received the full collection as it should
    this.blogsPaths = this.appUtilityService.getConfigurations();



    this.blogsPaths.forEach(blogPath =>
      this.appUtilityService.getBlogs(blogPath).subscribe(
        b =>
          b.forEach(blog => {
            this.blogs.push(blog);
          }),
        error => (this.errorMessage = <any>error)
      )
    );
  }

testConfiguration.json:

[
  "../../assets/BlogPosts/testBlog1.json",
  "../../assets/BlogPosts/testBlog2.json"
]

Bono si incluyes un buen tutorial sobre cómo funcionan las colecciones en JavaScript y cómo devolverlas correctamente

Respuesta #1

Resulta ser esto, sí, en serio...

Aquí lo hice bonito:

OnInit :

this.appUtilityService.getConfigurations().subscribe(
  b =>
    b.forEach(x => {
      this.appUtilityService.getBlogs(x).subscribe(
        b =>
          b.forEach(blog => {
            this.blogs.push(blog);
          }),
        error => (this.errorMessage = <any>error)
      );
    }),
  error => (this.errorMessage = <any>error)
);

ServicioDeUtilidadDeAplicación:

  getBlogs(blog: string): Observable<IBlogPost[]> {
    return this.http.get<IBlogPost[]>(blog);
  }

  getConfigurations(): Observable<string[]> {
    return this.http.get<string[]>(this.configuration);
  }

Hazlo realmente bonito:

  ngOnInit() {
    this.initializeBlog();
  }

  private initializeBlog(): void {
    this.appUtilityService.getConfigurations().subscribe(
      b =>
        b.forEach(x => {
          this.appUtilityService.getBlogs(x).subscribe(
            b =>
              b.forEach(blog => {
                this.blogs.push(blog);
              }),
            error => (this.errorMessage = <any>error)
          );
        }),
      error => (this.errorMessage = <any>error)
    );
  }

Respuesta #2

Dentro de getConfigurations, estás invocando una solicitud HTTP asincrónica. Así que es normal que configurationsData esté vacío fuera del método subscribe.

Aquí, estoy usando el poderoso RxJS para simplificar el código y evitar usar Array.push y subscribe() anidados.

1) Método de servicio async básico para obtener datos

Entonces getConfigurations debería devolver un Observable:

getConfigurations(): Observable<string[]> {
  return this.http.get<string[]>(this.configuration);
}

Y además getBlogs debería devolver un Observable:

getBlogs(blogsPath: string): Observable<BlogPost[]> {
  return this.http.get<BlogPost[]>(blogsPath);
}

Aquí, para respetar las mejores prácticas de Angular, quito I al comienzo de cada interfaz, así que BlogPost.

2) Aquí está la magia de RxJS

Luego, tienes otro método dentro de tu servicio para obtener un Observable de publicaciones de blog, que debería verse así:

getBlogPosts(): Observable<BlogPost[]> {
  return this.getConfigurations().pipe(
    mergeAll(),
    concatMap(blogsPath => this.getBlogs(blogsPath)),
    concatAll(),
    toArray()
  )
}

Lo que significa, recuperar toda la colección de rutas de blogs, para cada arreglo de rutas, para cada ruta, obtener las publicaciones del blog, y luego devolver un arreglo único de todas las publicaciones.

Finalmente, dentro de tu componente, llamas a getBlogPosts con una suscripción:

ngOnInit() {
  this.service.getBlogPosts().subscribe(blogs => {
    ...do something with your blogs array.
  });
}

o incluso mejor con async pipe dentro de tu plantilla :

archivo .ts:

blogs$: Observable<BlogPost[]>;
...
this.blogs$ = this.appService.getBlogPosts();

archivo .html:

<ul *ngIf="blogs$ | async as blogs">
    <li *ngFor="let blog of blogs">
        #{{ blog.id }} - {{ blog.title }}
    </li>
</ul>

Mira mi demostración completa de esta alternativa de RxJS en Stackbliz.

Lectura recomendada en Medium de Tomas Trajan