Мне просто интересно, можно ли использовать 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)
Мне кажется, что есть решение получше и более гибкое, но я не знаю, как это сделать лучше)
Хотя вы, безусловно, можете использовать redux, ngrx или какой-либо другой диспетчер состояний, если вы правильно используете области инжектора и наблюдаемые объекты, управления состоянием с помощью angular из коробки будет достаточно.
Просто примечание: обычно вам следует избегать публичного доступа к вашему Subject, а лучше возвращать его наблюдаемую часть: service.addressesUpdated.asObservable() в вашем случае.





Что ж, вы, конечно, можете это сделать. EventEmitter Angular также является предметом ниже (https://angular.io/api/core/EventEmitter). Однако, если вы чувствуете, что вам нужно делать это чаще, я бы порекомендовал посмотреть на решение для управления состоянием, такое как ngrx store: https://github.com/ngrx/platform
В профессиональном и личном плане у меня никогда не было хорошего опыта работы с ngrx. Это было излишне, много шаблонного кода, плохо масштабируется и действительно сводится к плохой архитектуре данных - только мое мнение.
Для меня это сработало очень хорошо, но вы правы, очень часто это перебор, и очень сложно сделать это правильно.
В 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. Это позволит синхронизировать представление родительского компонента с изменениями, внесенными вашим дочерним компонентом.
да, это нормально.