Search code examples
angularroutescomponentseventemitter

Angular2: Passing data between sibling components through routers?


I'm new to Angular2. I've been learning what I have mainly though the official docs, a slightly outdated udemy course by Mosh, and a book called ng-book2.

What I have is a form that always exists on the (top of the) page. Underneath it is a listing from the database. Clicking an item from the listing replaces the entire listing with details of that item. Clicking the back button takes you back to the listing. The form remains on top. It's a basic CRUD application. Submitting the form saves a new item to the database.

The problem is when I submit the form, the listing does not automatically get the new item: instead I have to refresh the page. Other operations (upvote, downvote, delete) are working fine.

app.component.html:

<app-article-form [listid]="listid" [formid]="formid"></app-article-form>
<router-outlet></router-outlet>

The router outlet displays either the item listing or an item detail.

Program architecture: I have a separate component for the form (ArticleFormComponent), a separate component for the listing (ArticlesComponent) and a separate component for detail (ArticleDetailComponent). Routing is between ArticlesComponent and ArticleDetailComponent.

I basically want ArticleFormComponent to notify it's sibling ArticlesComponent that a new Article has been submitted, and I want ArticlesComponent to receive that Article and push() it in the Articles[] array.

I googled a bit and tried to implement an emitter service to broadcast the event but the problem is that I'm using a router-outlet and do not know how to set input properties in that. Could someone guide me in the right direction? Thanks.


Solution

  • You could for example implement a PubSub pattern using simply RxJS's ReplySubject class. Here is how:

    import { Injectable } from '@angular/core';
    import { ReplaySubject } from 'rxjs';
    
    @Injectable()
    export class ArticlesPubSubService extends ReplaySubject<IArticle> {
    
      constructor() {
        super();
      }
    
    }
    

    Then use this ArticlesPubSubService in both components:

    1) in articles-form.component.ts you would emit the new created article:

    import { Component, OnInit } from '@angular/core';
    import { ArticlesPubSubService } from '../articles-pub-sub.service';
    
    @Component({
      selector: 'so-articles-form',
      templateUrl: './articles-form.component.html',
      styleUrls: ['./articles-form.component.css']
    })
    export class ArticlesFormComponent implements OnInit {
    
      constructor(private aps: ArticlesPubSubService) { }
    
      ngOnInit() {
      }
    
      submit(article) {
        // ... 
        this.aps.next(article);
      }
    
    }
    

    2) In articles.component.ts you would get this new article and push it to your local articles list:

    import { Component, OnInit } from '@angular/core';
    import { ArticlesPubSubService } from '../articles-pub-sub.service';
    
    @Component({
      selector: 'so-articles',
      templateUrl: './articles.component.html',
      styleUrls: ['./articles.component.css']
    })
    export class ArticlesComponent implements OnInit {
    
      articles: IArticles[];
    
      constructor(private aps: ArticlesPubSubService) { }
    
      ngOnInit() {
        this.aps.subscribe( article => this.articles.push(article) );
      }
      ngOnDestroy() {
        this.aps.unsubscribe();
      }
    }