Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

95
Views
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 answers
Answer question

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 Report

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 Report

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 Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!