How do I add an item to an Observable<Entity[]> type from an API response, if the Observable is being handled by the async pipe?

隐身守侯 提交于 2019-12-11 18:50:56

问题


So, I have the following service in an Angular 6 codebase:

export class GenericService<T> {
  public endpoint: string;
  constructor(private http: HttpClient) {}
  public create(entity: T): Observable<T> {
    return this.http.post<T>(`${environment.apiUrl}/api/${this.endpoint}`, entity);
  }
}

As you can see, the observable create(entity: T) method returns the entity it creates. In the component, it is handled thus:

@Component({
  selector: 'app-brand-dialog',
  templateUrl: './brand-dialog.component.html',
  styleUrls: ['./brand-dialog.component.css']
})
export class BrandDialogComponent implements OnInit {
  public addresses$ = Observable<Address[]>;
  readonly separatorKeysCodes: number[] = [ENTER, COMMA]
  constructor(private service: GenericService<Address>, @Inject(MAT_DIALOG_DATA) private brand: Brand){}
  ngOnInit(): void {
    this.addresses$ = this.service.getAll();
  }
  addAdress(address: Address) {
    this.service.create(address).subscribe((address) => {
     //address handling code would go here
    })
  }
}

And in the template:

<h2 mat-dialog-title>{{brand.name}}</h2>
<form (submit)="editbrand()">
  <mat-dialog-content>
    <mat-form-field>
      <input matInput placeholder="name" name="name" [(ngModel)]="brand.name" required>
    </mat-form-field><br />
    <mat-form-field *ngIf="brand.id">
      <mat-chip-list #chipList>
        <mat-chip *ngFor="let address of addresses$ | async" [selectable]="true" [removable]="true" (removed)="removeAddress(address)">
          {{address.name}}
          <mat-icon matChipRemove>cancel</mat-icon>
        </mat-chip>
        <input 
          matInput 
          placeholder="New address..."
          [matChipInputFor]="chipList"
          [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
          [matChipInputAddOnBlur]="false"
          (matChipInputTokenEnd)="addaddress($event)"
        >
      </mat-chip-list>
    </mat-form-field>
  </mat-dialog-content>
  <div mat-dialog-actions>
    <button mat-button (click)="onNoClick()">Cancel</button>
    <button mat-button type="submit" cdkFocusInitial>OK</button>
  </div>
</form>

As you can see, the Observable I use for the Addresses Angular Material chips is handled by Angular's async pipe, so I don't subscribe to it directly. However, my REST API returns a 201 Created with the newly created entity; I want to add it to addresses$ so that the async pipe catches and adds it to the chips list without having to do the whole request again. How would I go about it?


回答1:


The way I do it is something like this. It's just a POC, but it's very flexible. You can easily handle reloading all items, caching, etc. The idea is that the service handles the store of the items and the service also knows when a new item was added/removed/etc. to update the array. You can also add transaction support so that fake items appear that are not commited yet, undo support, etc.

export class GenericService<T> {
    private events$ = new ReplaySubject<Event>();
    public readonly items$ = concat(this.loadAllItems(), this.events$)
        .pipe(
            scan((items, event: Event) => {
                switch (event.type) {
                    case 'loaded':
                        return event.items;
                    case 'added':
                        return [...items, event.item];
                }
                return items;
            }, [] as T[]),
            shareReplay(1),
        );

    public endpoint: string; // where does this come from? you could use an interceptor...

    constructor(private http: HttpClient) {
    }

    public create(entity: T): Observable<T> {
        return this.http
            .post<T>(`${environment.apiUrl}/api/${this.endpoint}`, entity)
            .pipe(
                tap(_ => this.events$.next({ 
                  type: 'added', 
                  item: entity, // maybe you want the id from the response, or even the entire response...
                }))
            );
    }

    private loadAllItems() {
        return this.http
            .get<T>(`${environment.apiUrl}/api/${this.endpoint}`)
            .pipe(
                map(items => ({ type: 'loaded', items } as LoadedEvent))
            );
    }
}

type Event = LoadedEvent | AddedEvent;

interface AddedEvent {
    type: 'added';
    item: any;
}

interface LoadedEvent {
    type: 'loaded';
    items: any[];
}


来源:https://stackoverflow.com/questions/52722740/how-do-i-add-an-item-to-an-observableentity-type-from-an-api-response-if-th

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!