Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

93
Vistas
Pipe never gets executed when server response with new data

I created a subscription in my template to watch for changes to an object. The initial loading of the object displays the correct data for the property tags, when I add an item to the data it goes to a web server and returns a list of all the tags that are attached to the item (to keep the item in sync with the server). However, the newly added item isn't reflected on the page. I am not 100% sure why. I think it is because my of() statement but I am not sure. What I am seeing is that zip().pipe() never gets executed.

Do I need to use something other than of?

Note: I am trying to follow the declarative pattern to eliminate the usage of .subscribe()

Sub-Note: Once I get it working I plan on trying to remove the subscribe on this line this.server.file().subscribe

Stackblitz

export interface FileInfo {
  tags: string[];
}

@Component({
  selector: 'my-app',
  template: `
  <input #tag /><button (click)="addTag(tag.value)">Add Tag</button>

  <div *ngIf="data$ | async as data">
    <div *ngFor="let tag of data.tags">{{ tag }}</div>
  </div>
  `,
})
export class AppComponent {
  data$ = new Observable<FileInfo>();

  constructor(
    // Used to mimic server responses
    private readonly server: WebServer
  ) {}

  ngOnInit() {
    // I plan on removing this subscribe once I get a grasp on this
    this.server.file().subscribe((img) => {
      this.data$ = of(img);
    });
  }

  addTag(newTag: string) {
    const data$ = this.server.save(newTag);
    this.data$.pipe(concatMap((i) => this.zip(data$)));
  }

  private zip(tags$: Observable<string[]>) {
    return zip(this.data$, tags$).pipe(
      tap((i) => console.log('zipping', i)),
      map(([img, tags]) => ({ ...img, tags } as FileInfo))
    );
  }
}
about 4 years ago · Juan Pablo Isaza
3 Respuestas
Responde la pregunta

0

You're missusing the observable. After you subscribe with it, in the template with the async pipe, you should not update it's reference.

If you need to update the data, you must use a Subject.

export class AppComponent {
  private readonly data = new BehaviorSubject<FileInfo>(null);
  data$ = this.data.asObservable();

  constructor(
    // Used to mimic server responses
    private readonly server: WebServer
  ) {}

  ngOnInit() {
    this.server.file().subscribe((result) => this.data.next(result));
  }

  addTag(newTag: string) {
    this.server
      .save(newTag)
      .subscribe((tags) => this.data.next({ ...this.data.value, tags }));
  }
}

Also, your service could be a lot simpler:

@Injectable({ providedIn: 'root' })
export class WebServer {
  private readonly tags = ['dog', 'cat'];

  file(): Observable<FileInfo> {
    return of({ tags: this.tags });
  }

  save(tag: string) {
    this.tags.push(tag);
    return of(this.tags);
  }
}

Here's the working code:

https://stackblitz.com/edit/angular-ivy-my3wlu?file=src/app/app.component.ts

about 4 years ago · Juan Pablo Isaza Denunciar

0

It sounds like what you want to do is have a single observable source that emits the latest state of your object as new tags are added. Then you can simply subscribe to this single observable in the template using the async pipe.

In order to accomplish this, you can create a dedicated stream that represents the updated state of your file's tags.

Here's an example:

  private initialFileState$ = this.service.getFile();
  private addTag$ = new Subject<string>();
  private updatedfileTags$ = this.addTag$.pipe(
    concatMap(itemName => this.service.addTag(itemName))
  );

  public file$ = this.initialFileState$.pipe(
    switchMap(file => this.updatedfileTags$.pipe(
      startWith(file.tags),
      map(tags => ({ ...file, tags }))
    ))
  );

  constructor(private service: FileService) { }

  addTag(tagName: string) {
    this.addTag$.next(itemName);
  }

Here's a StackBlitz demo.

about 4 years ago · Juan Pablo Isaza Denunciar

0

Try completely converting webserver.service.ts to provide an observables of tags and FileInfo like this:

import { Injectable } from '@angular/core';
import { concat, Observable, of, Subject } from 'rxjs';
import { delay, map, shareReplay, tap } from 'rxjs/operators';
import { FileInfo } from './app.component'; // best practice is to move this to its own file, btw

@Injectable({ providedIn: 'root' })
export class WebServer {
  private fakeServerTagArray = ['dog', 'cat'];
  private readonly initialTags$ = of(this.fakeServerTagArray);
  private readonly tagToSave$: Subject<string> = new Subject();

  public readonly tags$: Observable<string[]> = concat(
    this.initialTags$,
    this.tagToSave$.pipe(
      tap(this.fakeServerTagArray.push),
      delay(100),
      map(() => this.fakeServerTagArray),
      shareReplay(1) // performant if more than one thing might listen, useless if only one thing listens
    )
  );

  public readonly file$: Observable<FileInfo> = this.tags$.pipe(
    map(tags => ({tags})),
    shareReplay(1) // performant if more than one thing might listen, useless if only one thing listens
  );

  save(tag: string): void {
    this.tagToSave$.next(tag);
  }
}

and now your AppComponent can just be

@Component({
  selector: 'my-app',
  template: `
  <input #tag /><button (click)="addTag(tag.value)">Add Tag</button>

  <div *ngIf="server.file$ | async as data">
    <div *ngFor="let tag of data.tags">{{ tag }}</div>
  </div>
  `,
})
export class AppComponent {
  constructor(
    private readonly server: WebServer;
  ) {}

  addTag(newTag: string) {
    this.server.save(newTag);
  }
}

Caveat: If you ever call WebServer.save while WebServer.tags$ or downstream isn't subscribed to, nothing will happen. In your case, no big deal, because the | async in your template subscribes. But if you ever split it up so that saving a tag is in a different component, the service will need to be slightly modified to ensure that the "save new tag" server API call is still made.

about 4 years ago · Juan Pablo Isaza Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda