0

I am working on angular 2. I found that the communication between the two sibling components or from child to parent is difficult. Even it was not mentioned in the angular 2 documentation

Would you please provide some information about it because the way two sibling components communicate is looking complex than the way a parent component communicates with its child using the 'ViewChild' decorator. But it is not possible to make a parent child relationship between two components always as per the project requirements.

<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script>
var finalvalue = {};
var increment=0;
var uniqueKey = [];
var divv;
function myFunction() {
var a=[" ","number","text","textbox","date"];  
   divv=document.createElement("div");
   divv.id="grandparentss"
   $("#demo").prop("disabled",true);
   main(a); 
  document.body.appendChild(divv);
}
function main(aa){
var div = document.createElement("div");
div.classList.add("class1");
var input = document.createElement("input");
input.id="input";
input.value="";
input.placeholder="label";
input.classList.add("design");
input.addEventListener("keyup", function(){addButtonFunction()});
var select = document.createElement("select");
select.id="select";
select.classList.add("design");
select.addEventListener("change", function(){addButtonFunction()});
var i=0;
for(i;i<aa.length;i++){
  var option = document.createElement("option");
  var txt = document.createTextNode(aa[i]);
  option.appendChild(txt);
  select.appendChild(option);
 }

var add = document.createElement("button");
add.id="button";
add.classList.add("design");
var txt1=document.createTextNode("add");
add.appendChild(txt1);
add.setAttribute("disabled","");
add.addEventListener("click", function(){ if(uniqueKey.indexOf($("#input").val()) === -1){ getValue($("#input").val(),$("#select").val())};result(input.value);$("#select").val(""); $("#input").val("");addButtonFunction()});
var submit = document.createElement("button");
  submit.id = "submit"
  submit.classList.add("design");
  var txt2=document.createTextNode("submit"); 
  submit.appendChild(txt2);
  submit.setAttribute("disabled","");
  submit.addEventListener("click", function(){submiting()});
  div.appendChild(input);
  div.appendChild(select);
  div.appendChild(add);
  div.appendChild(submit);
  divv.appendChild(div); 
}


function addButtonFunction(){
  if($("#input").val() && $("#select").val()){
        document.getElementById("button").removeAttribute("disabled");
    } 
    else{
        document.getElementById("button").setAttribute("disabled","");
    }

}

function getValue(key,value){
  var div = document.createElement("div");
  div.id= key+"11"+increment;
  div.classList.add("form-inline");
  var div1 = document.createElement("div"); 
  div1.classList.add("form-group");
  div1.id= key+"$"+increment;
  var label = document.createElement("label");
  var txt = document.createTextNode(key);
  label.appendChild(txt);
  if(value === "textbox"){
  var input = document.createElement("textarea");
  }
  else{
  var input = document.createElement("input");
  input.classList.add("form-control");
  if(value === "date"){input.type="date"
  }
  else if(value === "number"){input.type="number"}
  input.placeholder="please write"+ key;
  }
  input.classList.add("form-control");
  input.id=key;
  input.addEventListener("input", function(){result(input.id);$(`#${input.id}`).val()});
  div1.appendChild(label);
  div1.appendChild(input);
  var add = document.createElement("button");
  var txt1=document.createTextNode("x");
  add.appendChild(txt1);
  add.addEventListener("click", function(){deleted(div.id,div1.id); deleteJson(input.id)});
  div1.appendChild(add);
  div.appendChild(div1);
  divv.appendChild(div);
  increment++;
}

function result(input){
 finalvalue[input]= $(`#${input}`).val();
 uniqueKey=Object.keys(finalvalue);
 if(Object.keys(finalvalue).length){document.getElementById("submit").removeAttribute("disabled");}else{document.getElementById("button").setAttribute("disabled","");}

}

function deleted(parent,child){
 document.getElementById(parent).removeChild(document.getElementById(child));
 document.getElementById("grandparentss").removeChild(document.getElementById(parent));
}

function deleteJson(input){
 delete finalvalue[input];
 uniqueKey=Object.keys(finalvalue);
}

function submiting(){
 document.getElementById("output").innerHTML= JSON.stringify(finalvalue);
   console.log(finalvalue);
}
</script>

<style>

.class1{
margin-top: 47px;
margin-bottom: 20px;
margin-left: 44px;
}

.form{
margin-top: 21px;
margin-left: 20px;

}
.form-inline{
    margin-top: 47px;
    margin-bottom: 20px;
    margin-left: 95px;
    }
.design{
margin-left: 57px;
}
.form-control{
margin-left: 65px;
}
</style>
</head>
<body>
<button onclick="myFunction()" class="form" id="demo">create Form</button>
<br><br>
<p id="output"></P>
</body>
</html>

4 Answers4

1

Your structure should be like this

parent
  |-- Sibling 1
  |-- Sibling 2

If this is the case, you can use the ViewChild and Host directives.

In the parent :

@ViewChild(ChildOneComponent) one: ChildOneComponent;
@ViewChild(ChildTwoComponent) two: ChildTwoComponent;

In your children :

constructor(@Host() public parent: ParentComponent)

Now in The first child, you can use this :

this.parent.one.methodFromChildOne();

This is one of many examples, probably not the best one (thight coupling, gna gna gna), but it is the easiest one to pull and understand I think.

  • If it is parent child means we can use this thanks for your replay...nice answer can you have any working example for this or any link to understand –  Mar 14 '18 at 05:27
0

You can use state management like mobx-angular, ngrx-store, or you can use @Input, @Output or two-way binding [(variable)], depends on you needs.

tano
  • 2,657
  • 1
  • 15
  • 16
0

Or you can use two-way service. Useful even when your components are in different module.

Service

import { Injectable } from '@angular/core';
import { Subject }    from 'rxjs/Subject';

@Injectable()
export class AppShareService {
  private readonly subjectSource$ = new Subject<object>();

  public get newData(): Observable<object> {
    return this.subjectSource$.asObservable();
  }

  public publish(data: any) {
    this.subjectSource$.next(data);
  }
}

and you can publish event-like messages like this:

export class AppComponent {
  constructor(public appShareService: AppShareService ) {
    appShareService.publish({data: 'some data'});
  }
}

and you can subscribe to these events:

export class HomeComponent implements OnDestroy {
  mySubscription: Subscription;

  constructor(public appShareService: AppShareService ) {
    this.mySubscription = appShareService.newData.subscribe((data) => {
      console.log(data); // {data: 'some data'}
    });
  }

ngOnDestroy(): void {
  if (this.mySubscription) {
    this.mySubscription.unsubscribe();
  }
 }
}

Good practice is to unsubscribe from Observable always. and ngOnDestroy is good place for that.

DiPix
  • 5,755
  • 15
  • 61
  • 108
  • Thanks Bro... This i need but I like to know is there any need to import this service in both module. –  Mar 14 '18 at 05:05
  • @Guvaliour I didn't notice your question before. Actually this service should be in `CoreModule`. And `CoreModule` is always imported in `AppModule`. So you should have access everywhere in application. Please mark this answer if it helped you :) You can read more about module structure here: https://stackoverflow.com/questions/42695931/angular2-coremodule-vs-sharedmodule – DiPix May 28 '18 at 07:17
0

One can also use @Input/@Output and Observables and EventEmitters. This helps with OnPush change detection. if you use default change detection then it's even easier. You could use the shared service technique. Below sample with @Input@Output in children and parent and Observables and async pipe to subscribe.

EXAMPLE:

    @Component({
    selector: 'parent',
    template: `<div><notes-grid 
            [Notes]="(NotesList$ | async)"
            (selectedNote)="ReceiveSelectedNote($event)"
        </notes-grid>
        <note-edit 
            [gridSelectedNote]="(SelectedNote$ | async)"
        </note-edit></div>`,
    styleUrls: ['./parent.component.scss']
})
export class ParentComponent {

    // create empty observable
    NotesList$: Observable<Note[]> = of<Note[]>([]);
    SelectedNote$: Observable<Note> = of<Note>();

    //passed from note-grid for selected note to edit.
    ReceiveSelectedNote(selectedNote: Note) {
    if (selectedNote !== null) {
        // change value direct subscribers or async pipe subscribers will get new value.
        this.SelectedNote$ = of<Note>(selectedNote);
    }
    }
    //used in subscribe next() to http call response.  Left out all that code for brevity.  This just shows how observable is populated.
    onNextData(n: Note[]): void {
    // Assign to Obeservable direct subscribers or async pipe subscribers will get new value.
    this.NotesList$ = of<Note[]>(n.NoteList);  //json from server
    }
}

//child 1 sibling
@Component({
  selector: 'note-edit',
  templateUrl: './note-edit.component.html', // just a textarea for noteText and submit and cancel buttons.
  styleUrls: ['./note-edit.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class NoteEditComponent implements OnChanges {
  @Input() gridSelectedNote: Note;

    constructor() {
    }

// used to capture @Input changes for new gridSelectedNote input
ngOnChanges(changes: SimpleChanges) {
     if (changes.gridSelectedNote && changes.gridSelectedNote.currentValue !== null) {      
      this.noteText = changes.gridSelectedNote.currentValue.noteText;
      this.noteCreateDtm = changes.gridSelectedNote.currentValue.noteCreateDtm;
      this.noteAuthorName = changes.gridSelectedNote.currentValue.noteAuthorName;
      }
  }

}

//child 2 sibling

@Component({
    selector: 'notes-grid',
    templateUrl: './notes-grid.component.html',  //just an html table with notetext, author, date
    styleUrls: ['./notes-grid.component.scss'],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class NotesGridComponent {

// the not currently selected fromt eh grid.
    CurrentSelectedNoteData: Note;

    // list for grid
    @Input() Notes: Note[];

    // selected note of grid sent out to the parent to send to sibling.
    @Output() readonly selectedNote: EventEmitter<Note> = new EventEmitter<Note>();

    constructor() {
    }

    // use when you need to send out the selected note to note-edit via parent using output-> input .
    EmitSelectedNote(){
    this.selectedNote.emit(this.CurrentSelectedNoteData);
    }

}


// here just so you can see what it looks like.

export interface Note {
    noteText: string;
    noteCreateDtm: string;
    noteAuthorName: string;
}
howserss
  • 1,139
  • 1
  • 8
  • 12