Angular 2 Sibling Component Communication

前端 未结 12 1287
深忆病人
深忆病人 2020-11-22 06:31

I have a ListComponent. When an item is clicked in ListComponent, the details of that item should be shown in DetailComponent. Both are on the screen at the same time, so

相关标签:
12条回答
  • 2020-11-22 07:02

    Behaviour subjects. I wrote a blog about that.

    import { BehaviorSubject } from 'rxjs/BehaviorSubject';
    private noId = new BehaviorSubject<number>(0); 
      defaultId = this.noId.asObservable();
    
    newId(urlId) {
     this.noId.next(urlId); 
     }
    

    In this example i am declaring a noid behavior subject of type number. Also it is an observable. And if "something happend" this will change with the new(){} function.

    So, in the sibling's components, one will call the function, to make the change, and the other one will be affected by that change, or vice-versa.

    For example, I get the id from the URL and update the noid from the behavior subject.

    public getId () {
      const id = +this.route.snapshot.paramMap.get('id'); 
      return id; 
    }
    
    ngOnInit(): void { 
     const id = +this.getId ();
     this.taskService.newId(id) 
    }
    

    And from the other side, I can ask if that ID is "what ever i want" and make a choice after that, in my case if i want to delte a task, and that task is the current url, it have to redirect me to the home:

    delete(task: Task): void { 
      //we save the id , cuz after the delete function, we  gonna lose it 
      const oldId = task.id; 
      this.taskService.deleteTask(task) 
          .subscribe(task => { //we call the defaultId function from task.service.
            this.taskService.defaultId //here we are subscribed to the urlId, which give us the id from the view task 
                     .subscribe(urlId => {
                this.urlId = urlId ;
                      if (oldId == urlId ) { 
                    // Location.call('/home'); 
                    this.router.navigate(['/home']); 
                  } 
              }) 
        }) 
    }
    
    0 讨论(0)
  • 2020-11-22 07:04

    One way to do this is using a shared service.

    However I find the following solution much simpler, it allows to share data between 2 siblings.(I tested this only on Angular 5)

    In you parent component template:

    <!-- Assigns "AppSibling1Component" instance to variable "data" -->
    <app-sibling1 #data></app-sibling1>
    <!-- Passes the variable "data" to AppSibling2Component instance -->
    <app-sibling2 [data]="data"></app-sibling2> 
    

    app-sibling2.component.ts

    import { AppSibling1Component } from '../app-sibling1/app-sibling1.component';
    ...
    
    export class AppSibling2Component {
       ...
       @Input() data: AppSibling1Component;
       ...
    }
    
    0 讨论(0)
  • 2020-11-22 07:07

    Updated to rc.4: When trying to get data passed between sibling components in angular 2, The simplest way right now (angular.rc.4) is to take advantage of angular2's hierarchal dependency injection and create a shared service.

    Here would be the service:

    import {Injectable} from '@angular/core';
    
    @Injectable()
    export class SharedService {
        dataArray: string[] = [];
    
        insertData(data: string){
            this.dataArray.unshift(data);
        }
    }
    

    Now, here would be the PARENT component

    import {Component} from '@angular/core';
    import {SharedService} from './shared.service';
    import {ChildComponent} from './child.component';
    import {ChildSiblingComponent} from './child-sibling.component';
    @Component({
        selector: 'parent-component',
        template: `
            <h1>Parent</h1>
            <div>
                <child-component></child-component>
                <child-sibling-component></child-sibling-component>
            </div>
        `,
        providers: [SharedService],
        directives: [ChildComponent, ChildSiblingComponent]
    })
    export class parentComponent{
    
    } 
    

    and its two children

    child 1

    import {Component, OnInit} from '@angular/core';
    import {SharedService} from './shared.service'
    
    @Component({
        selector: 'child-component',
        template: `
            <h1>I am a child</h1>
            <div>
                <ul *ngFor="#data in data">
                    <li>{{data}}</li>
                </ul>
            </div>
        `
    })
    export class ChildComponent implements OnInit{
        data: string[] = [];
        constructor(
            private _sharedService: SharedService) { }
        ngOnInit():any {
            this.data = this._sharedService.dataArray;
        }
    }
    

    child 2 (It's sibling)

    import {Component} from 'angular2/core';
    import {SharedService} from './shared.service'
    
    @Component({
        selector: 'child-sibling-component',
        template: `
            <h1>I am a child</h1>
            <input type="text" [(ngModel)]="data"/>
            <button (click)="addData()"></button>
        `
    })
    export class ChildSiblingComponent{
        data: string = 'Testing data';
        constructor(
            private _sharedService: SharedService){}
        addData(){
            this._sharedService.insertData(this.data);
            this.data = '';
        }
    }
    

    NOW: Things to take note of when using this method.

    1. Only include the service provider for the shared service in the PARENT component and NOT the children.
    2. You still have to include constructors and import the service in the children
    3. This answer was originally answered for an early angular 2 beta version. All that has changed though are the import statements, so that is all you need to update if you used the original version by chance.
    0 讨论(0)
  • 2020-11-22 07:08

    I have been passing down setter methods from the parent to one of its children through a binding, calling that method with the data from the child component, meaning that the parent component is updated and can then update its second child component with the new data. It does require binding 'this' or using an arrow function though.

    This has the benefit that the children aren't so coupled to each other as they don't need a specific shared service.

    I am not entirely sure that this is best practice, would be interesting to hear others views on this.

    0 讨论(0)
  • 2020-11-22 07:10

    You need to set up the parent-child relationship between your components. The problem is that you might simply inject the child components in the constructor of the parent component and store it in a local variable. Instead, you should declare the child components in your parent component by using the @ViewChild property declarator. This is how your parent component should look like:

    import { Component, ViewChild, AfterViewInit } from '@angular/core';
    import { ListComponent } from './list.component';
    import { DetailComponent } from './detail.component';
    
    @Component({
      selector: 'app-component',
      template: '<list-component></list-component><detail-component></detail-component>',
      directives: [ListComponent, DetailComponent]
    })
    class AppComponent implements AfterViewInit {
      @ViewChild(ListComponent) listComponent:ListComponent;
      @ViewChild(DetailComponent) detailComponent: DetailComponent;
    
      ngAfterViewInit() {
        // afther this point the children are set, so you can use them
        this.detailComponent.doSomething();
      }
    }
    

    https://angular.io/docs/ts/latest/api/core/index/ViewChild-var.html

    https://angular.io/docs/ts/latest/cookbook/component-communication.html#parent-to-view-child

    Beware, the child component will not be available in the constructor of the parent component, just after the ngAfterViewInit lifecycle hook is called. To catch this hook simple implement the AfterViewInit interface in you parent class the same way you would do with OnInit.

    But, there are other property declarators as explained in this blog note: http://blog.mgechev.com/2016/01/23/angular2-viewchildren-contentchildren-difference-viewproviders/

    0 讨论(0)
  • 2020-11-22 07:17

    A directive can make sense in certain situations to 'connect' components. In fact the things being connected don't even need to be full components, and sometimes it's more lightweight and actually simpler if they aren't.

    For example I've got a Youtube Player component (wrapping Youtube API) and I wanted some controller buttons for it. The only reason the buttons aren't part of my main component is that they're located elsewhere in the DOM.

    In this case it's really just an 'extension' component that will only ever be of use with the 'parent' component. I say 'parent', but in the DOM it is a sibling - so call it what you will.

    Like I said it doesn't even need to be a full component, in my case it's just a <button> (but it could be a component).

    @Directive({
        selector: '[ytPlayerPlayButton]'
    })
    export class YoutubePlayerPlayButtonDirective {
    
        _player: YoutubePlayerComponent; 
    
        @Input('ytPlayerVideo')
        private set player(value: YoutubePlayerComponent) {
           this._player = value;    
        }
    
        @HostListener('click') click() {
            this._player.play();
        }
    
       constructor(private elementRef: ElementRef) {
           // the button itself
       }
    }
    

    In the HTML for ProductPage.component, where youtube-player is obviously my component that wraps the Youtube API.

    <youtube-player #technologyVideo videoId='NuU74nesR5A'></youtube-player>
    
    ... lots more DOM ...
    
    <button class="play-button"        
            ytPlayerPlayButton
            [ytPlayerVideo]="technologyVideo">Play</button>
    

    The directive hooks everything up for me, and I don't have to declare the (click) event in the HTML.

    So the directive can nicely connect to the video player without having to involve ProductPage as a mediator.

    This is the first time I've actually done this, so not yet sure how scalable it might be for much more complex situations. For this though I'm happy and it leaves my HTML simple and responsibilities of everything distinct.

    0 讨论(0)
提交回复
热议问题