26

I have this small gridComponent:

@Component({
    selector: 'moving-grid',
    templateUrl: './grid.component.html',
    styleUrls: ['./grid.component.css']
})
  export class GridComponent {
     @Input('widgets') extComponents: Array<Component>;
  }

And a second testComponent

@Component({
  selector: 'test',
  template: `
    <div #content>I say hello<li>i</li><li>u</li><button (click)="test()">click me</button> world</div>
  `
})

export class TestComponent {
  @ViewChild('content') content: HTMLElement;

  getContent() {
    return this.content;
  }
  test() {
    console.log("test");
  }
}

Now I'm trying to pass multiple instances of testComponent to the gridComponent. Therefore I have a third Component which looks like this one:

selector: 'moving-grid-container',
template: `
        <moving-grid [widgets]="[z1,z2]"> 
          <test  class="grid-item-content-001" #z1></test>
          <test  class="grid-item-content-002" #z2></test>
        </moving-grid>
      `

Until this point, everything works like expected. But how can I render the Components from @Input in the gridComponent? My first approach was to declare a @ViewChild in the testComponent and return it with a getContent()-function. But it won't work. Can I use the ng-content directive in some way or is there a better solution?

The GridComponent looks like this. I want to display the templates of a @Input-Component inside one of the black boxes. Is it possible?

Moving-Grid Component

Thanks for any kind of help

1

1 Answer 1

29

You should not use an @Input to pass in the components. You can use @ContentChildren for that and an abstract WidgetComponent:

@Component({
    selector: 'moving-grid',
    template: `
      <div class="widget-wrapper">
         <ng-container *ngFor="let widget of widgets">
             <!-- use a ngSwitchCase here for different types-->
             <grid-test-widget [widget]="widget" *ngIf="widget.active && widget.type === 'test'"></grid-test-widget>
         </ng-container>          
      </div>
    `,
    styleUrls: ['./grid.component.css']
})
export class GridComponent implements AfterContentInit {
     @ContentChildren('widget')
     widgets: QueryList<WidgetComponent>;

     ngAfterContentInit() {
        //your components will be available here
     }
}

The template of your third component [moving-grid-container] will stay the same, but without the [widgets] and an added #widget name:

<moving-grid> 
  <test-widget class="grid-item-content-001" #widget [active]="false"></test-widget>
  <test-widget class="grid-item-content-002" #widget></test-widget>
</moving-grid>

Your TestWidgetComponent which will extend an abstract WidgetComponent :

@Component({
  selector: 'test-widget',
  // ...
})
export class TestWidgetComponent extends WidgetComponent {
    public type: string = 'test';
}

And your WidgetComponent:

@Directive()
export abstract class WidgetComponent {

   @Input()
   public active: boolean;

   public type: string;

}

And you'll have several grid widgets based on the type of the widget:

@Component({
  selector: 'grid-test-widget',
  template: `<div #content>I say hello<li>i</li><li>u</li><button (click)="test()">click me</button> world</div>`
})
export class GridTestWidgetComponent{}
    
Sign up to request clarification or add additional context in comments.

9 Comments

Thanks for the answer. But can I add the components to the template by code? I have to store the components and display them on some kind of event. Also it would be much better if the gridComponent doesn't know anything about the testComponent (no import tag)
The problem with dynamic adding stuff, is that it kinda prevents you from using AoT, and it's also not seen as the angular way. Perhaps you should try to find another way to achieve what you want. I'll update my answer a little, to show how can you dynamically turn on and off a widget. But I'm not exactly sure what you want to achieve
I've updated my answer. Hopes it makes sense a bit. But no, it's not really possible to do what you want: 'store the content'. You will lose the angular bindings if you use [innerHtml] for instance. I hope my answer gives you ideas to do it another way
Thanks for the effort. Think I can't even get a better solution for this problem.
Why this use case isn't mentioned in the angular documentation is beyond me. Thanks for the clear explaination!
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.