Tell me how to transfer data between child or non-native components? It seems like you can through the services, but I can not understand the logic of how to register. For example, I need to transfer the date from one component to another.

  • I'm still trying to get into these tips, until I can say whether or not I helped, I understand =) - Teapot

1 answer 1

The data between the transfer in different ways, you can isolate the logic in the service, but this is an resort to decomposition. It is possible through binding and event generators ( EventEmitter ), and it is possible through native dispatching. Imagine such a tree:

 -- app-calendar -- app-events -- app-event-list -- app-event 

We want to transfer data from the app-event component to the app-calendar , what would it look like using an event generator?

 -- app-calendar (подписывается на генератор из `app-events`) -- app-events (подписывается на генератор из `app-event-list` + создает свой генератор для `app-calendar`) -- app-event-list (подписывается на генератор из `app-event` + создает генератор для `app-events`) -- app-event (создает генератор для `app-event-list`) 

enter image description here

In the end, we need to create generators in three components at different levels, it will be similar to pasta. You can avoid this by using the usual event toggleEvent , for example, we want the app-calendar component to listen to the toggleEvent event, which is an app-event (located at the very bottom of the tree). To do this, we inject a link to ourselves into the app-event component and dispatch the event:

 @Component({ selector: 'app-event', template: ` <p>Пойду ли я на это событие? - {{ event.status }}</p> <button (click)="toggleEvent()">Изменить статус</button> ` }) class EventComponent { @Input() public event: CityEvent = null!; constructor(private host: ElementRef<HTMLElement> {} public toggleEvent(): void { const event = new CustomEvent<CityEvent>('toggleEvent', { detail: this.event, bubbles: true <-- magic :) }); this.host.nativeElement.dispatchEvent(event); } } 

PS In IE11, you need to use document.createEvent('CustomEvent') + initCustomEvent(...) .

In the app-calendar component template (which is at the very top of the tree), we can tell EventManager to listen to this event:

 @Component({ selector: 'app-calendar', template: ` <app-events (toggleEvent)="toggleEvent($event)"></app-events> ` }) class CalendarComponent { public toggleEvent({ detail }: CustomEvent<SystemEvent>): void { console.log(detail); } } 

Notice that the app-event is inside the app-event-list , but the event may float up to app-calendar . This all works thanks to the zone and the compiler, the result of the compiler will be optimized JavaScript code, which will contain ready-made information for initializing the directive:

 <app-events (toggleEvent)="toggleEvent($event)"></app-events> | AOT компилятор | elementDef(..., ..., ..., 'app-events', ..., ..., [['toggleEvent']] <-- свойство `outputs`) | Angular 8 (компилятор Ivy) | elementStart(index, 'app-events', attrs); listener('toggleEvent', (event) => componentInstance.toggleEvent(event); elementEnd(); 

Therefore, in runtime, the component, or rather the wrapper on top of the component ( ViewDef ), knows which event to listen to, the pseudo-code below:

 const events = document.querySelector('app-events'); events.addEventListener('toggleEvent', (event: CustomEvent) => { viewDef.componentInstance.toggleEvent(event); }); 

In the case of the new Angular and Ivy compiler, in runtime, the event handler is registered immediately after creating the app-events element, for this R3 instructions and incremental DOM are used.

I prefer this option, as it saves me from allocating a large amount of memory. Because:

  • Observable - 96 bytes
  • Subject - 96 bytes
  • EventEmitter - 104 bytes
  • Subscription - 104 bytes
  • Subscriber - 104 bytes
  • Event - 56 bytes
  • MouseEvent - 40 bytes

This is a large chain of inheritance, only each entity has its own business logic. EventEmitter is an abstraction over Subject :

 class EventEmitter extends Subject ... 

The only difference is the possibility of asynchronous event generation:

 new EventEmitter(true <-- `__isAsync`); 

Therefore, in cases where __isAsync === true - next simply turns into setTimeout .

The second option is communication through services. Communication through the services was laid back in the beta of the second Angular, as Mixko proposed SCA (service-component architecture). The implementation is very simple, the service creates some kind of event generator:

 public events$ = new Subject<unknown>(); 

Components subscribe to this generator, and also manually generate some events through:

 service.events$.next(что-то); 

As an example, there are components that are generally located in different modules, but when you click on a button, you need to hide the sidebar, how to implement it using the service?

 @Injectable({ providedIn: 'root' }) export class SidebarService { public sidebarShown$ = new BehaviorSubject<boolean>(false); } 

BehaviorSubject is a generator that caches the last event.

 @Component({ template: ` <app-sidebar *ngIf="sidebarShown$ | async"></app-sidebar> ` }) export class SomeComponentOnSomeLevel { public sidebarShown$ = this.sidebarService.sidebarShown$.asObservable(); constructor(private sidebarService: SidebarService) {} } 

In the component of another module we also inject this service and generate an event per click:

 @Component({ template: ` <button (click)="toggleSidebar()">Toggle sidebar</button> ` }) export class AnotherComponentOnAnotherLevel { constructor(private sidebarService: SidebarService) {} public toggleSidebar(): void { const shown = this.sidebarService.sidebarShown$.getValue(); this.sidebarService.sidebarShown$.next(!shown); } } 

When there are many such services there is a high probability of encountering entropy and single responsibility begins to bend.

The best option, even though you are a beginner, but still have to try it in action - Redux :) Redux is an architectural template that allows you to reduce the connection between the components of the system through a one-way data stream coming from one storage. Angular has many tools for Redux, such as NgRx , NGXS , Akita , redux-observable . Redux enables DDD and scalability. There is such a diagram, it reflects the Redux-oriented application. enter image description here

If you ever mature, then I advise you to this article , it will give you a small excursion into the most popular tools for Redux in Angular.