【问题标题】:Why isn't @ContentChildren getting populated in my custom Angular 2 dropdown?为什么我的自定义 Angular 2 下拉列表中没有填充 @ContentChildren?
【发布时间】:2017-01-03 18:14:36
【问题描述】:

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

  1. 我正在使用 ngModel
  2. 下拉组件在查询其子级之前并不知道它们的存在。下拉项将位于其他模板中,因为“其他”将包含需要插入的数据。

这是我目前所拥有的 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 是空的,我该如何解决?我在这里错过了什么? (我想我可以只使用服务在 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

【问题讨论】:

    标签: angular drop-down-menu typescript


    【解决方案1】:

    制作

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

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

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

    【讨论】:

      【解决方案2】:

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

      但是对于您的特定问题,有一个使用HostListener 的解决方法

      或者使用MutationObserver

      【讨论】:

      • 未解决的问题是由于(重复)嵌套,而 HostListener 用于连接到属性指令所附加的元素,但 MutationObserver 看起来很有希望,以蛮力的方式。跨度>
      猜你喜欢
      • 2016-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-06
      • 1970-01-01
      • 1970-01-01
      • 2016-05-10
      • 2018-06-17
      相关资源
      最近更新 更多