0

I would like to aggregate statically defined child components with dynamical once that might be sourced from the stream. I want a generic way to define carousel in this carousel where I can have a typed collection of components. The collection can be defined statically or dynamically. Below is an example:

import 'package:angular/angular.dart';
import 'package:angular/core.dart';
import 'package:angular_components/angular_components.dart';


/**
 *
 */
@Component(
    selector: 'md-text',
    styleUrls: const ['md_text.css'],
    template: '<li>{{name}}</li>',
    directives: const [
      materialDirectives,
    ]
)
class TextComponent implements OnInit{
  @Input()
  String name;

  TextComponent([this.name]);

  TextComponent.withName(String name)
      : this(name);

  @override
  ngOnInit() {
  }
}

@Component(
    selector: 'md-texts',
    styleUrls: const ['text_component.css'],
    template: '<ol><md-text *ngFor="let component of components" [name]="component.name"></md-text><ng-content></ng-content></ol>',
    directives: const [
      CORE_DIRECTIVES,
      materialDirectives,
      TextComponent
    ]
)
class TextsComponent<TextComponent> implements OnInit
{
  Set<T> _components;

  Set<T> get components => _components;

  addComponent(T component){
    this._components.add(component);
  }

  removeComponent(T component) {
    this._components.remove(component);
  }

  TextsComponent() {
    this._components = new LinkedHashSet<T>();
    TextComponent declarativeChannel = new TextComponent.withName("United States");
    this.addComponent(declarativeChannel);
  }

  @override
  ngOnInit(){
    print("I am here");
  }
}

in my dashboard component, I have statically defined the usage as follows:

<md-texts>
    <md-text [name]="'Liberty'"></md-text>
    <md-text [name]="'Life'"></md-text> 
<md-texts>

The display is as following United States Liberty Life

What I want to do is have the "Liberty" and "Life" also be aggregated to my component collection, so I can control it with the next and previous button. I also only want to render them when their index is called for. What is the best way to do this in AngularDart.

I found a similar question with old version but with older version How to reference child component from its parent in AngularDart in AD 1.0.0 and How to reference child component from its parent in AngularDart in AD 1.0.0

Regards, Hopefully i have explained my problem and i will get clear direction on what is the correct way of addressing this design issue.

TiKi
  • 15
  • 3
  • I don't really understand what you ask for, but it might be similar to https://stackoverflow.com/questions/36325212/angular-2-dynamic-tabs-with-user-click-chosen-components/36325468#36325468 – Günter Zöchbauer Dec 07 '17 at 16:15
  • @GünterZöchbauer , my question is little different, I know how to use dynamic components. But the question is how can a static component definition be detected (discovered) by the parent component. In my case Texts is parent component Text is child components. When i use it statically in markup how can they be added to my collection components in above example? If you look at the code i have injecting United States instance via constructor and Life, Liberty thru markup, the components collection is only aware of United States how can i make it aware of Life and Liberty. – TiKi Dec 07 '17 at 17:02
  • I think you need an approach like shown in my linked answer. Just add them dynamically, then you can control them. You can't move around components added statically (still not sure I understand what you try to accomplish) – Günter Zöchbauer Dec 07 '17 at 18:39

1 Answers1

2

You can use @ViewChildren and @ContentChildren to query for child components. @ViewChildren will query for components declared in your template, whereas @ContentChildren will query for components projected into an <ng-content> in your template.

It's worth mentioning that in your example, your dynamic TextComponent is being created twice. First, you create one in your constructor, then again when you declare a <md-text> in your template. This means the instance you're keeping a reference to, isn't actually the component rendered in your view. To avoid this, don't bother creating the instance imperatively. Just keep a model of the data needed to create them, then pass that through inputs in your template where you declare the components. You can then use @ViewChildren to query for those components you've created.

Here's an example

@Component(
  selector: 'md-texts',
  template: '''
    <ol>
      <md-text *ngFor="let text of texts" [text]="text"></md-text>
      <ng-content></ng-content>
    </ol>
  ''',
  directives: const [NgFor, TextComponent],
)
class TextsComponent {
  /// Text that was projected through `<ng-content>`.
  @ContentChildren(TextComponent)
  QueryList<TextComponent> contentTexts;

  /// Text that was created in our view from our model.
  @ViewChildren(TextComponent)
  QueryList<TextComponent> viewTexts;

  /// Data model for the text in our view.
  final List<String> texts = [
    ...
  ];
}

Alternatively, you could render all of the <md-text> from a single source of data in your view, and rely on passing in a model for the static data rather than projecting the components.

Leon Senft
  • 270
  • 1
  • 5
  • your answer was correct, I wish there was a way to aggregate both viewchild and contentchild as one, but I do agree with your comments. Ideally, it would just make sense to have all the components be rendered from a single data input list or a stream. – TiKi Dec 08 '17 at 01:45