Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

100
Visualizações
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 Respostas
Responde à pergunta

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 Relatório

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 Relatório

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 Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda