Как мне добавить предмет в Observableтип из ответа API, если Observable обрабатывается асинхронным каналом? - PullRequest
0 голосов
/ 09 октября 2018

Итак, у меня есть следующий сервис в кодовой базе Angular 6:

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);
  }
}

Как видите, наблюдаемый метод create(entity: T) возвращает созданную им сущность.В компоненте он обрабатывается следующим образом:

@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
    })
  }
}

А в шаблоне:

<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>

Как вы можете видеть, наблюдаемое мной для чипов Адреса углового материала обрабатываетсяАсинхронный канал Angular, поэтому я не подписываюсь на него напрямую.Однако мой REST API возвращает 201 Created с вновь созданным объектом;Я хочу добавить его к адресам $, чтобы асинхронный канал перехватывал и добавлял его в список микросхем без необходимости повторного выполнения всего запроса.Как бы я пошел по этому поводу?

1 Ответ

0 голосов
/ 09 октября 2018

То, как я это делаю, выглядит примерно так.Это просто POC, но он очень гибкий.Вы можете легко справиться с перезагрузкой всех элементов, кэшированием и т. Д. Идея состоит в том, что служба обрабатывает хранилище элементов, и служба также знает, когда был добавлен / удален новый элемент / и т.д.обновить массив.Вы также можете добавить поддержку транзакций, чтобы отображались поддельные элементы, которые еще не переданы, отмена поддержки и т. Д.

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[];
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...