0
votes

I have parent component with child directive inside of it. Needs to get input data from the component in the directive.

<my-parent [data]="'Hello from parent'">
  <input myChild>
</my-parent>

Parent Component

@Component({
  selector: 'my-parent',
  template: `
    <ng-content select="[myChild]"></ng-content>
  `
})
export class ParentComponent {
  @Input() data: string;
}

Child Directive

@Directive({
  selector: '[myChild]'
})
export class ChildDirective implements OnInit {
  value: string;

  ngOnInit() {
    // needs to get data from parent component here
  }
}

I can inject parent component into directive, but I don't want to do this, because it increases the risk of cyclical dependencies.

I can use @ContentChild in ParentComponent and pass data in AfterContentInit via public property, but I am not sure when this property will be available in ChildDirective, should I use AfterContentChecked for this case or not?

Parent Component

@ContentChild(ChildDirective, {static: true}) child: ChildDirective;

ngAfterContentInit() {
  this.child.value = this.data;
}

Child Directive

value: string;

ngAfterContentChecked() {
  // will be available data from parent here?
}

Maybe I should use service to share data between the component and the directive?

https://stackblitz.com/edit/angular-pass-data-to-child-directive

2

2 Answers

0
votes

Is there a particular reason you need to be using ng-content instead of an ng-template?

I suggest you use an approach with ng-templates as follows

Parent Component

@Component({
  selector: 'my-parent',
  template: `
    <ng-template 
      [ngTemplateOutlet]="child" 
      [ngTemplateOutletContext]="{ $implicit: data }">
    </ng-template>
  `
})
export class ParentComponent implements AfterContentInit {
  @Input() data: string;
  @ContentChild(TemplateRef, {static: true}) child: TemplateRef<any>;
}

Child Directive

@Directive({
  selector: '[myChild]'
})
export class ChildDirective {
  @Input()
  value: string;
}

Usage

<my-parent [data]="'Hello from parent'">
   <ng-template let-dataThroughParent>
      <input myChild [value]="dataThroughParent" class="form-control">
   </ng-template>
</my-parent>

IMPORTANT

In this specific case (of the example) it is not really necessary to bind the data to your parent, then get it through the template variable and then bind it again to your "myChild" directive as it would be redundant and unnecessary, if your use case resembles this, I would pass the data directly to "myChild" directive as follows:

<my-parent [data]="'Hello from parent'">
   <ng-template>
      <input myChild [value]="'Hello from parent'" class="form-control">
   </ng-template>
</my-parent>

Note that it would work for your initial approach too by doing :

<my-parent [data]="'Hello from parent'">
  <input myChild [value]="'Hello from parent'">
</my-parent>

But I am assuming you truly need to pass data from the parent that is not accessible in the scope where you use the parent. Here is a working example

https://stackblitz.com/edit/angular-pass-data-to-child-directive-ebcbg6

0
votes

You can pass data to the directive with @Input() binding.

app.component.html

<my-parent [data]="'Hello from parent'"></my-parent>

my-parent.component.ts

@Component({
    selector: 'my-parent',
    templateUrl: './my-parent.component.html',
    styleUrls: ['./my-parent.component.scss'],
})
export class MyParentComponent implements OnInit {

   @Input() data: string;
}

my-parent.component.html

<input [myChild]="data"/>

my-child.directive.ts

@Directive({
  selector: '[myChild]'
})
export class ChildDirective implements OnInit {
  value: string;

@Input('myChild') data: string;

  ngOnInit() {
      console.log(this.data) // data from parent
  }
}


https://angular.io/guide/attribute-directives