Подраздел Angular и rxjs для управления состоянием

Мне просто интересно, можно ли использовать Subject в службе для уведомления компонента об изменении состояния. Приведу пример. Например. У меня один компонент adresses-list.component.ts это просто список адресов. При щелчке по определенному адресу его полная информация отображается справа от списка или в модальном окне. Это будет называться "edit-address.component.ts" Таким образом, в этом компоненте пользователь может редактировать адрес и сохранять изменения. Конечно, я понимаю, что будет запрос на обновление в бэкенд. Однако проблема в том, что мой adresses-list.component.ts не будет знать об этих изменениях, потому что он подписан на наблюдаемые адреса, а не на изменения. Итак, мой вопрос: Это нормальная практика: в моем service.ts:

addressesUpdated = new Subject();
editAddressById(id) {
// send info to DB
  return this.http.patch().map(_ => {this.addressesUpdated.next())
}

В моих адресах-list.component.ts

service.addressesUpdated.switchMap( _ => service.getAddresses())
.subscribe(...save data here)

Мне кажется, что есть решение получше и более гибкое, но я не знаю, как это сделать лучше)

да, это нормально.

David Anthony Acosta 11.10.2018 19:15

Хотя вы, безусловно, можете использовать redux, ngrx или какой-либо другой диспетчер состояний, если вы правильно используете области инжектора и наблюдаемые объекты, управления состоянием с помощью angular из коробки будет достаточно.

Rafael 11.10.2018 19:23

Просто примечание: обычно вам следует избегать публичного доступа к вашему Subject, а лучше возвращать его наблюдаемую часть: service.addressesUpdated.asObservable() в вашем случае.

Jeto 11.10.2018 20:58
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
0
3
261
2

Ответы 2

Что ж, вы, конечно, можете это сделать. EventEmitter Angular также является предметом ниже (https://angular.io/api/core/EventEmitter). Однако, если вы чувствуете, что вам нужно делать это чаще, я бы порекомендовал посмотреть на решение для управления состоянием, такое как ngrx store: https://github.com/ngrx/platform

В профессиональном и личном плане у меня никогда не было хорошего опыта работы с ngrx. Это было излишне, много шаблонного кода, плохо масштабируется и действительно сводится к плохой архитектуре данных - только мое мнение.

Rafael 11.10.2018 19:27

Для меня это сработало очень хорошо, но вы правы, очень часто это перебор, и очень сложно сделать это правильно.

Christian S. 11.10.2018 20:05

В Angular это можно сделать с помощью двусторонней привязки модели с помощью @Output EventEmitter. Получает ли ваш дочерний компонент для редактирования адресов свою модель представления из привязки attr к тегу селектора? Если так, то вы на полпути. Вот как я бы реализовал то, чего вы хотите достичь.

Модели

export class AddressModel {
    public Street1: string;
    public Street2: string;
    public City: string;
    public StateProvince: string;
    public PostalCode: string;
    public CountryCode: string;
}

export class ApiResponse {
    public Success: boolean;
    public ErrorMessage: string;
}

Родительский компонент

@Component({
    selector: 'addressListComponent',
    templateUrl: './addressList.html';
})

export class AddressListComponent implements OnInit {
    addressList: AddressModel[];
    addressSelected: boolean[];

    constructor(private api: AddressApi){}

    ngOnInit(): void {
        this.AddressList = new AddressList();
        this.api.GetAddressList()
            .then(response: AddressModel[] => {
                this.addressList = response.slice(0);
                this.addressList.forEach(addr => {
                    this.addressSelected.push(false);
                });
            });
    }
}

Родительский вид

<div class = "card" *NgFor = "let address of addressList; let i = "index"; [attr.data-index] = "i" (click) = "addressSelected[i]=!addressSelected[i]">
    <div class = "card-body">
        <label>{{address.Street1}}</label>
        <label>{{address.Street2}}</label>
        <label>{{address.City}}</label>
        <label>{{address.StateProvince}}</label>
        <label>{{address.PostalCode}}</label>
        <label>{{address.Country}}</label>
    </div>
    <editAddressComponent *ngIf = "addressSelected[i]" [(addressModel)] = "addressList[i]"></editAddressComponent>
</div>

Редактировать дочерний компонент адреса

@Component({
    selector: 'editAddressComponent',
    templateUrl: './editAddress.html'
})

export class EditAddressComponent {
    @Input() addressModel: AddressModel;
    @Output() addressModelChanged: EventEmitter<AddressModel> = new EventEmitter<AddressModel>();

    constructor(private api: AddressApi) { }

    saveChanges(): void {
        this.api.saveAddressChanges(this.addressModel)
            .then(response: ApiResponse => {
                if (response.Success === true) {
                    this.addressModelChanged.emit(this.addressModel);
                } else {
                    alert(response.ErrorMessage);
                }
            });
    }
}

Просмотр адреса редактирования ребенка

<div class = "card">
    <div class = "card-body">
        <input [(ngModel)] = "addressModel.Street1"/>
        <input [(ngModel)] = "addressModel.Street2"/>
        <input [(ngModel)] = "addressModel.City"/>
        <input [(ngModel)] = "addressModel.StateProvince"/>
        <input [(ngModel)] = "addressModel.PostalCode"/>
        <input [(ngModel)] = "addressModel.Country"/>
        <button (click) = "saveChanges()"></button>
    </div>
</div>

Обратите внимание на синтаксис [()] в родительском представлении. Вот где происходит волшебство. Когда у вас есть собственный атрибут @Input для компонента и вы передаете ему значение через [], это односторонняя привязка.

Если вы обернете настраиваемый атрибут в [()], как я сделал с addressModel, это будет коротко для

<editAddressComponent [addressModel] = "addressList[i]" (addressModelChanged) = "addressList[i] = $event;"></editAddressComponent>

Это означает, что за кулисами Angular настраивает ваш дочерний компонент для поддержки эмиттера событий, что и представляет собой синтаксис шаблона ().

Брать

<button (click) = "doStuff();"></button>

Например.

() обозначает eventEmitter, и вы назначаете ему, что делать, когда он генерируется, в этом случае выполните метод doStuff ().

Итак, используя

<editAddressComponent [(addressModel)] = "addressList[i]"></editAddressComponent>

Вы настраиваете дочерний компонент для поддержки EventEmitter, теперь вам просто нужно его создать.

Вот что делает @Output. Он регистрирует используемую переменную в вашем дочернем компоненте как тип EventEmitter, который специально генерирует объект AddressModel.

Теперь, когда пользователь сохраняет свои изменения, если изменения были сохранены успешно, вы используете EventEmitter для генерации события, передающего измененный addressModel в качестве объекта, который нужно захватить.

Затем в родительском компоненте вы захватываете объект и назначаете его своему списку адресов по индексу i. Это позволит синхронизировать представление родительского компонента с изменениями, внесенными вашим дочерним компонентом.

Другие вопросы по теме