为什么@ContentChildren不会在我的自定义Angular 2下拉列表中填充?

时间:2016-08-27 02:58:47

标签: angular drop-down-menu typescript

我正在尝试复制Fabric样式下拉列表的外观,以便在Angular 2中使用。有两个重要的部分我正在努力做到:

  1. 我正在使用ngModel
  2. 下拉组件在查询之前不知道其子项。下拉项目将在其他模板中,因为它是“其他东西”,它将需要插入数据。
  3. 以下是我目前为dropdown和dropdownItem组件所做的事情:

    @Component({
        selector: "dropdown",
        template: ` <div class="ms-Dropdown" [ngClass]="{'ms-Dropdown--open': isOpen}">
                        <span class="ms-Dropdown-title" (click)="toggleDropdown()"> {{selectedName}} </span>
                        <ul class="ms-Dropdown-items">
                            <ng-content></ng-content>
                        </ul>
                    </div>`,
        providers: [QueryList]
    })
    export class FabricDropdownComponent extends AbstractValueAccessor implements AfterContentInit {
        public selectedName: string;
        public isOpen: boolean;
        private subscriptions: Subscription[];
        constructor( @ContentChildren(FabricDropdownItemComponent) private items: QueryList<FabricDropdownItemComponent>) {
            super();
            this.subscriptions = [];
            this.selectedName = "Filler text, this should be replaced by 'Thing'";
            this.isOpen = false;
        }
    
        // HERE'S THE PROBLEM: this.items is an empty array, so I can't find any child components.
        public ngAfterContentInit() {
            this.items.changes.subscribe((list: any) => {
                // On every change, re-subscribe.
                this.subscriptions.forEach((sub: Subscription) => sub.unsubscribe());
                this.subscriptions = [];
                this.items.forEach((item: FabricDropdownItemComponent) => {
                    this.subscriptions.push(item.onSelected.subscribe((selected: INameValuePair) => {
                        this.value = selected.value;
                        this.selectedName = selected.name;
                        this.isOpen = false;
                    }));
                });
            });
    
            // During init, display the *name* of the selected value.
            // AGAIN: items is empty, can't set the initial value. What's going on?
            this.items.forEach((item: FabricDropdownItemComponent) => {
                if (item.value === this.value) {
                    this.selectedName = item.name;
                }
            })
        }
    
        public toggleDropdown() { 
            this.isOpen = !this.isOpen;
        }
    }
    
    @Component({
        selector: "dropdownItem",
        template: `<li (click)="select()" class="ms-Dropdown-item" [ngClass]="{'ms-Dropdown-item--selected': isSelected }">{{name}}</li>`
    })
    export class FabricDropdownItemComponent implements OnInit {
        @Input() public name: string;
        @Input() public value: any;
        @Output() public onSelected: EventEmitter<INameValuePair>;
        public isSelected: boolean;
        constructor() {
            this.onSelected = new EventEmitter<INameValuePair>();
            this.isSelected = false;
        }
    
        public ngOnInit() {
            if (!this.name) {
                this.name = this.value.toString();
            }
        }
    
        public select() {
            this.onSelected.emit({ name: this.name, value: this.value });
            this.isSelected = true;
        }
    
        public deselect() {
            this.isSelected = false;
        }
    }
    

    (AbstractValueAccessor来自this other answer。)

    以下是我在应用程序中实际使用它们的方式:

    <dropdown [(ngModel)]="responseType" ngDefaultControl>
        <dropdownItem [value]="'All'"></dropdownItem>
        <dropdownItem *ngFor="let r of responseTypes" [value]="r.value" [name]="r.name"></dropdownItem>
    </dropdown>
    

    我的问题是@ContentChildren的下拉列表的QueryList始终为空,因此当我点击其中一个dropdownItem时,它不会收到通知。为什么QueryList为空,我该如何解决?我在这里错过了什么? (我想我可以使用服务在dropdown和dropdownItem之间进行通信,而不是QueryList,但这不是我在这里问的问题 - 为什么QueryList为空?)

    我尝试过使用@ViewChildren,但这不起作用。我尝试将FabricDropdownItemComponent添加到下拉列表的指令中,但这只是给出了一个不同的错误:Error: Unexpected directive value 'undefined' on the View of component 'FabricDropdownComponent'

    Plunker:https://plnkr.co/edit/D431ihORMR7etrZOBdpW?p=preview

2 个答案:

答案 0 :(得分:1)

有一个与@ContentChildren相关的开放issue

但是对于您的特定问题,使用HostListener

有一种解决方法

或使用MutationObserver

答案 1 :(得分:0)

@ContentChildren(FabricDropdownItemComponent) private items: QueryList<FabricDropdownItemComponent>

您的类的属性而不是构造函数参数

export class FabricDropdownComponent extends AbstractValueAccessor implements AfterContentInit {
  @ContentChildren(FabricDropdownItemComponent) private items: QueryList<FabricDropdownItemComponent>
  constructor() {}
  ....
}
相关问题