Set property of child component from parent to render render

I am trying to set a child property of a component from a parent before viewing it.

Now I am using the ViewChildren decorator in the parent component, but in this case it is too late because the view of the children has to use the data that is coming from the parent.

So the grid class contains a complete reference to the grid object, and I want to bind it to the grid-header to the grid-header-item.

Root html:

<grid-header [grid]="grid" [ngClass.xs]="{'search-open': grid.searchToggle, 'mobile-header': true}">
    <grid-header-item fxFlex="75px" name="Level"></grid-header-item>
    <grid-header-item fxFlex name="Type"></grid-header-item>
    <grid-header-item fxFlex name="Stats"></grid-header-item>
</grid-header>

      

Parent component:

import { Component, ViewChildren, forwardRef, Input, OnInit, AfterViewInit, OnDestroy } from '@angular/core';

import { 
    StorageService, 
} from '../../_shared';

import { 
    GridHeaderItemComponent, 
} from './';

@Component({
    selector: 'grid-header',
    templateUrl: './grid-header.component.html',
    styleUrls: ['./grid-header.component.scss'],
    host: {'fxLayout': 'row'}
})
export class GridHeaderComponent implements OnInit, AfterViewInit {

    constructor(storage: StorageService) {}

    @Input() grid = null;
    @ViewChildren(forwardRef(() => GridHeaderItemComponent)) private headerItem;

    public ngOnInit() {

    }

    public ngAfterViewInit() {

        console.log(this.headerItem);

        setTimeout(() => {
            this.headerItem.grid = this.grid;
        }, 0);
    }
}

      

child component:

import { Component, HostBinding, Input, OnInit, OnDestroy } from '@angular/core';

import { 
    StorageService, 
} from '../../_shared';

@Component({
    selector: 'grid-header-item',
    templateUrl: './grid-header-item.component.html',
    styleUrls: ['./grid-header-item.component.scss'],
    host: {

    }
})
export class GridHeaderItemComponent implements OnInit {
    public grid;

    constructor(storage: StorageService) {}

    @Input() name: string = '';

    public ngOnInit() {
        console.log(this.name);
    }

    public ngOnDestroy() {
        // this.storage.unsubscribeAll(this.class);
    }
}

      

child componententetn html:

<span class="item" (click)="grid.onSortBy(name);">
    {{name}} <span *ngIf="grid.sort == name" class="fa" [ngClass]="grid.getDirection(name)"></span>
</span>

      

I know if I add [grid] = "grid" to the grid-header-item it works, but I think it's an overhead because all children with grid headers need to get this link, so it's redundant.

<grid-header [grid]="grid" [ngClass.xs]="{'search-open': grid.searchToggle, 'mobile-header': true}">
    <grid-header-item fxFlex="75px" [grid]="grid" name="Level"></grid-header-item>
    <grid-header-item fxFlex [grid]="grid" name="Type"></grid-header-item>
    <grid-header-item fxFlex [grid]="grid" name="Stats"></grid-header-item>
</grid-header>

      

+3


source to share


2 answers


Since angular2 has a dependency injection system based on the component tree, we can simply inject the parent component into the child component:

Grid-header.component.ts

export class GridHeaderItemComponent implements OnInit {
    public grid;
    @Input() name: string = '';

    constructor(private parent: GridHeaderComponent) { }

      

and then assign the property to a parent.grid

local property grid

in ngOnInit



Grid-header.component.ts

 public ngOnInit() {
   this.grid = this.parent.grid;
 }

      

You can also see it in action in the Plunger Example

+1


source


With this solution, you will have access to the data in ngAfterViewInit, but you will not have "circural dependecy" (parent β†’ <-child). If you need more detailed communication, try using events or a shared service.

gridHeader.component.ts

@Component({
    selector: 'grid-header-item',
    template: `
        <span class="item" (click)="grid.onSortBy(name);">
            {{name}} <span *ngIf="grid.sort == name">Sortby</span>
        </span>
    `
})
export class GridHeaderItemComponent{
    @Input() name: string = '';
    public grid:any = {};
}

@Component({
    selector: 'grid-header',
    template:  `
    <grid-header-item fxFlex="75px" name="Level"></grid-header-item><br/>
    <grid-header-item fxFlex name="Type"></grid-header-item><br/>
    <grid-header-item fxFlex name="Stats"></grid-header-item>`
})
export class GridHeaderComponent implements AfterViewInit{   
  @ViewChildren(GridHeaderItemComponent) private headers:Array<headerItem>;   
  @Input() grid = {};
    ngAfterViewInit() {
      this.headers.forEach(h=>{//it can be a QueryList too
        h.grid=this.grid;
    });
}}

      



And in the grid wrapper component you have something like this:

@Component({
  selector: 'my-app',
  template: `<grid-header [grid]="grid"></grid-header>`,
})
//...

      

0


source







All Articles