How to pass object from one component to another in Angular 2?
Asked Answered
U

5

85

I have Angular components and first component uses the second one as a directive. They should share the same model object, which is initialized in the first component. How can I pass that model to the second component?

Unkind answered 4/12, 2015 at 12:15 Comment(2)
Can you post some code? Generally, you use template local variables for this kind of thing in ng2, but it's hard to say without a little more detail on your part.Moody
I found this answer helpful: https://mcmap.net/q/234878/-how-do-i-share-data-between-components-in-angular-2Eda
B
26

Component 2, the directive component can define a input property (@input annotation in Typescript). And Component 1 can pass that property to the directive component from template.

See this SO answer How to do inter communication between a master and detail component in Angular2?

and how input is being passed to child components. In your case it is directive.

Bowhead answered 4/12, 2015 at 12:27 Comment(2)
Can i use it for non parent-child components? For example <navbar></navbar> <router-outlet></router-outlet> And i want all components in router-outlet have an access to navbar component, is it possible?Burier
@Burier In order to achieve that, the object you are sharing has to be in the common parent of those two components. There's no other way to share an object among sibling components, which is what you described.Batory
E
88

For one-way data binding from parent to child, use the @Input decorator (as recommended by the style guide) to specify an input property on the child component

@Input() model: any;   // instead of any, specify your type

and use template property binding in the parent template

<child [model]="parentModel"></child>

Since you are passing an object (a JavaScript reference type) any changes you make to object properties in the parent or the child component will be reflected in the other component, since both components have a reference to the same object. I show this in the Plunker.

If you reassign the object in the parent component

this.model = someNewModel;

Angular will propagate the new object reference to the child component (automatically, as part of change detection).

The only thing you shouldn't do is reassign the object in the child component. If you do this, the parent will still reference the original object. (If you do need two-way data binding, see https://mcmap.net/q/247447/-bidirectional-data-binding-on-a-component-input-property).

@Component({
  selector: 'child',
  template: `<h3>child</h3> 
    <div>{{model.prop1}}</div>
    <button (click)="updateModel()">update model</button>`
})
class Child {
  @Input() model: any;   // instead of any, specify your type
  updateModel() {
    this.model.prop1 += ' child';
  }
}

@Component({
  selector: 'my-app',
  directives: [Child],
  template: `
    <h3>Parent</h3>
    <div>{{parentModel.prop1}}</div>
    <button (click)="updateModel()">update model</button>
    <child [model]="parentModel"></child>`
})
export class AppComponent {
  parentModel = { prop1: '1st prop', prop2: '2nd prop' };
  constructor() {}
  updateModel() { this.parentModel.prop1 += ' parent'; }
}

Plunker - Angular RC.2

Engler answered 8/12, 2015 at 3:30 Comment(5)
you're doing god's work! any advice for sibling components? in my case I have 2 at root level bootstrapped. HeaderComponent has a search input which i want to share with components in the body..Alluvion
@SonicSoul, put the data in a JavaScript reference type in the parent, or, if they don't share a parent, put the data in a shared service. For the service approach, you can again used a JavaScript reference type or use observables.Engler
thanks! I am trying the service route.. didn't think of actually putting the Observable on itAlluvion
@Mark we have learned Angular1 with your posts and know again you are teaching us Angular2, you rock :)Stevenage
I posted a two way databinding example here : #31027386Bachelorism
B
26

Component 2, the directive component can define a input property (@input annotation in Typescript). And Component 1 can pass that property to the directive component from template.

See this SO answer How to do inter communication between a master and detail component in Angular2?

and how input is being passed to child components. In your case it is directive.

Bowhead answered 4/12, 2015 at 12:27 Comment(2)
Can i use it for non parent-child components? For example <navbar></navbar> <router-outlet></router-outlet> And i want all components in router-outlet have an access to navbar component, is it possible?Burier
@Burier In order to achieve that, the object you are sharing has to be in the common parent of those two components. There's no other way to share an object among sibling components, which is what you described.Batory
W
20

you could also store your data in an service with an setter and get it over a getter

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

@Injectable()
export class StorageService {

    public scope: Array<any> | boolean = false;

    constructor() {
    }

    public getScope(): Array<any> | boolean {
        return this.scope;
    }

    public setScope(scope: any): void {
        this.scope = scope;
    }
}
Waki answered 5/4, 2017 at 7:18 Comment(5)
Your answer is too short but it is the right one for no parent-child components.Arvell
This is a great idea. I am so glad I found this answer. It is especially good if you know that your object is pretty much globally used in many components across the page.Kamacite
getScope() is returning 'undefined' when I access it after setting it from other page. It sets the variable fine, but can't retrieve it back! Any ideas?Kosygin
Not understandable.Sty
This is the best solution, so much code and time saver! perfect! it works like a star!!!!Odor
D
3

From component

import { Component, OnInit, ViewChild} from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    import { dataService } from "src/app/service/data.service";
    @Component( {
        selector: 'app-sideWidget',
        templateUrl: './sideWidget.html',
        styleUrls: ['./linked-widget.component.css']
    } )
    export class sideWidget{
    TableColumnNames: object[];
    SelectedtableName: string = "patient";
    constructor( private LWTableColumnNames: dataService ) { 
       
    }
    
    ngOnInit() {
        this.http.post( 'getColumns', this.SelectedtableName )
            .subscribe(
            ( data: object[] ) => {
                this.TableColumnNames = data;
     this.LWTableColumnNames.refLWTableColumnNames = this.TableColumnNames; //this line of code will pass the value through data service
            } );
    
    }    
    }

DataService

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';

@Injectable()
export class dataService {
    refLWTableColumnNames: object;//creating an object for the data
}

To Component

import { Component, OnInit } from '@angular/core';
import { dataService } from "src/app/service/data.service";

@Component( {
    selector: 'app-linked-widget',
    templateUrl: './linked-widget.component.html',
    styleUrls: ['./linked-widget.component.css']
} )
export class LinkedWidgetComponent implements OnInit {

    constructor(private LWTableColumnNames: dataService) { }

    ngOnInit() {
    console.log(this.LWTableColumnNames.refLWTableColumnNames);
    }
    createTable(){
        console.log(this.LWTableColumnNames.refLWTableColumnNames);// calling the object from another component
    }

}
Demimondaine answered 26/6, 2019 at 7:18 Comment(0)
B
2

Use the output annotation

@Directive({
  selector: 'interval-dir',
})
class IntervalDir {
  @Output() everySecond = new EventEmitter();
  @Output('everyFiveSeconds') five5Secs = new EventEmitter();
  constructor() {
    setInterval(() => this.everySecond.emit("event"), 1000);
    setInterval(() => this.five5Secs.emit("event"), 5000);
  }
}
@Component({
  selector: 'app',
  template: `
    <interval-dir (everySecond)="everySecond()" (everyFiveSeconds)="everyFiveSeconds()">
    </interval-dir>
  `,
  directives: [IntervalDir]
})
class App {
  everySecond() { console.log('second'); }
  everyFiveSeconds() { console.log('five seconds'); }
}
bootstrap(App);
Bombard answered 16/3, 2016 at 15:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.