Обновление пользовательского интерфейса с помощью АСИНХРОННОГО канала и возврат наблюдаемого

#angular #rxjs #observable

#angular #rxjs #наблюдаемый

Вопрос:

Я нахожусь в довольно затруднительном положении, и это мог бы быть подход, который я использовал, изучая RxJS и наблюдаемые.

Предисловие

Наше приложение использует Angular для подключения к нашему общедоступному уровню API (C #). Тип объекта, который возвращается из моего АСИНХРОННОГО вызова, имеет Observable< ListResultDtoOfNoteDto >. Этот поток передается дочернему компоненту, который перебирает элементы. Этот же поток также позволяет пользователю добавлять новые заметки типа NoteDto или редактировать заметки. В идеале, когда эти действия будут выполнены, список заметок будет отражать обновленные изменения без необходимости повторного вызова той же службы для получения заметок.

Код

[notes.component.html ]

 <div *ngIf="(notes$ | async)?.items as notes; else loading">
    <note-card [noteData]="notes" (noteClick)="editNote($event)"></note-card>
</div>
<ng-template #loading>
    <div class="notes__container">
        <div class="note-card">
            <div class="note-card__primary-action">
                <div class="note-card__head">
                    <h2 class="note-card__title note-card-typography--headline1 text-center">Loading...</h2>
                </div>
            </div>
        </div>
    </div>
</ng-template>
  

[примечания.component.ts]

 import ...

@Component({
    ...
})
export class NotesComponent extends AppComponentBase implements OnInit {
    constructor(
        ...
        private _noteService: NoteServiceServiceProxy
    ) {
        super(injector);
    }

    public notes$: Observable<ListResultDtoOfNoteDto>;
    public userId: number;
    ...

    ngOnInit() {
        ...
        this.getNotes();
        ...
    }

    getNotes(): void {
        /*
            Binding to ASYNC pipe; which doesn't need a subscription since it is automatic?
            Returns type: Observable<ListResultDtoOfNoteDto> which is an array of NoteDto
        */
        this.notes$ = this._noteService.getUserNotes(this.userId, null);
        ...
    }
    ...

    public saveNote(note: NoteDto, mode: string) {
        if (mode === 'new') {
            this._noteService.addNote(note).subscribe(() => {
                this.cleanUp();
                this.getNotes();
                this.notify.info(this.l('SavedNoteSuccessfully'));
            });
        } 
        ...
    }
}
  

Вся функциональность за вычетом ClickEvent в дочернем элементе выполняется внутри родительского элемента. Который загружает форму (методология реактивной формы), которая может создавать новую заметку и сохранять или редактировать старую заметку для обновления.

Моя проблема

Мне просто нужен способ сохранить заметку и обновить список без необходимости снова вызывать this.getNotes() в подписке при добавлении новой заметки.

(При необходимости я могу предоставить более подробную информацию!)

РЕДАКТИРОВАТЬ: Забыл показать метод сохранения в данный момент

Комментарии:

1. содержит ли ваш сервис notes прямой вызов api? если вы не хотите снова вызывать getNotes, вам нужно будет сохранить данные в сервисе и обновить эти данные после выполнения вызова add (обычно с помощью HTTP POST для создания объекта вы получите созданный объект обратно и, следовательно, сможете добавить этот объект в свой сервис, содержащий ваши данные). Если ваше приложение (многие компоненты) сильно полагаются на эту базу данных, возможно, стоит проверить github.com/ngrx/store в противном случае вы могли бы просто написать одноэлементный сервис, содержащий ваши данные.

2. Я не делаю этого технически в рамках this.notes $ = this._noteService.getUserNotes(<>) в моем методе getNotes()? Также, если вы посмотрите на мой метод SaveNotes (), я не думаю, что использую ASYNC должным образом, поскольку я снова вызываю GetNotes () и снова переделываю этот вызов службы

Ответ №1:

Один из способов, которым вы можете достичь этого, — иметь частный BehaviorSubject внутри вашего NoteServiceServiceProxy , который всегда будет содержать последнее значение notes.

Нравится: private _notes = new BehaviorSubject<ListResultDtoOfNoteDto>(null)

  • и вы публикуете функцию, которая предназначена только для подписки потребителей.
  • Ваш вызов API обновит тему только с помощью ответа
 public getNotes(): Observable<ListResultDtoOfNoteDto> {
    return this._notes.asObservable();
}

public getNotesFromApi(args): {
    // Api request
    this.httpClient.get().pipe(tap(result) => this._notes.next(result))
    // etc...
}
  

Затем все, что вам нужно сделать в вашем компоненте, это объявить ваш наблюдаемый объект примерно так

notesData = this. _noteService.getNotes();

внутри init просто сделайте запрос к API, чтобы получить заметки, и он автоматически обновит ваш, notesData который должен быть подписан на ваш шаблон через async вывод канала.

При сохранении все, что вам нужно сделать, это просто добавить concatMap в конце, чтобы сначала сохранить, а затем запросить обновление из вашего api.

Итак, что-то вроде

     this._noteService.addNote(note).pipe(
        concatMap(() => this._noteService.getNotesFromApi()))
        .subscribe();
  

Это снова обновит вашу переменную notesData, которая находится в шаблоне.

Я надеюсь, вы понимаете, как работает этот поток, и если нет, не стесняйтесь спрашивать.

Надеюсь, это поможет!

Комментарии:

1. Итак, API написан на C #, и мы используем продукт ASP .NET Zero, в котором есть процесс, преобразующий C # в автоматически сгенерированный JS (Angular) в качестве сервисов. Таким образом, сервисы предварительно сгенерированы и передаются непосредственно в вызовы API. Возможно ли было бы выполнить первую 1/2 вашего ответа в notes.component.ts? Поток имеет полный смысл, я просто не могу изменить автоматически сгенерированный сервис-прокси, поскольку будущие изменения просто перезапишут файл.

2. Да, я имею в виду, что если у вас есть какие-то ограничения, то вам нужно настроить его на свой вкус. Я думаю, что в вашем сценарии наличие состояния внутри компонента также выполнило бы эту работу. Поскольку поток сохраняется, но разделения нет.

3. Решая эту проблему, я откорректировал ваш ответ и создал объект, подобный частному хранилищу данных, из которого я мог бы отправлять и извлекать данные и привязывать обратно к созданному нами observable, который извлекается из общедоступной переменной в качестве объекта async. Теперь все работает на удивление хорошо, и мне не нужно снова вручную обращаться к API, чтобы обновить компонент. Спасибо!