3
votes

I have a component called button-search which has a dropdown with search options:

<button-search> 
    <item type="identifier">Identifier</item>
    <item type="title">Title</item>
    <item type="city">City</item>
    <item type="town">Town</item>
    <item type="address">Address</item>
    <item type="postal">Postal</item>
    <item type="divider"></item>
    <item type="clear">Clear Search</item>
</button-search>

The item component is not supposed to render anything directly and is rather for passing complex params into the button-search component so that the button-search component can render those dropdown items the way it should be rendered.

Item is defined as follow:

@Component(
    selector: 'item',
    template: '')
class Item {

    @Input() String type = "type-goes-here";

}

ButtonSearch is defined as follow:

@Component(
    selector: 'button-search',
    directives: const[Item],
    template: '''
       ... enormous template ...    
    ''')
class ButtonSearch {

    ButtonSearch(@ViewChildren(Item) QueryList<Item> items){

        print(items);

    }
}

Instead of seeing a list of 9 items being printed to the console, all I'm seeing is [].

I've tried using a String param instead of an object, but it still gives me null.

ButtonSearch(@ViewChildren('item') QueryList<Item> items){
  1. What am I missing to make @ViewChildren get all the items and print something other than []

  2. Is there something special that needs to be done to get the text between <item> and </item> or will @ViewChild work for that ?

Update: Changing the template to include <ng-content></ng-content>:

template: '''
          <ng-content></ng-content>
           ... enormous template ...    
        ''')

I can see the following being printed in the browser as part of the button-search component:

Identifier, Title, City, Town, Address, Postal, Clear Search

So at least I know the page I'm on does have items in its button-search component.

1
Please check if the Item components get instantiated. You can't query for Item if the elements are not upgrade to actual Angular components.Günter Zöchbauer

1 Answers

5
votes

It should be

@Component(
    selector: 'button-search',
    directives: const[Item],
    template: '''
       <ng-content></ng-content>
       ... enormous template ...    
    ''')
class ButtonSearch implements AfterContentInit{
    @ContentChildren(Item) QueryList<Item> items;

    ngAfterContentInit() {
        print(items);
    }    
}

See also https://stackoverflow.com/a/35209681/217408

There is also the @Query() annotation which allows to constructor-inject references to child elements but it is marked deprecated.

<ng-content>
If you pass "content" as children to your component, then you can query them using @ContentChildren() instead of @ViewChildren(). If you want them to be displayed in the DOM you also need to add the <ng-content></ng-content> tag as a target for the transclusion. You can also have multiple <ng-content></ng-content> tags where a select=".someCssSelector" can be used to transclude a subset of content at a specific target location. There should be only one <ng-content></ng-content> without the select attribute though. The first without select will target all content that doesn't match other selectors.

Plunker example