QueryList未使用动态组件

时间:2017-05-13 00:06:41

标签: angular

我有一个父组件,它可以有同一子组件的多个实例。这里棘手的部分是子组件的实例在init处可用并且基于子组件内的某些逻辑我正在使用输出事件发射器从父组件重新生成另一个子组件。但是,保存时我只看到我放在html模板中的子组件的第一个实例而不是动态生成的子组件。我确实看到了其他类似的问题,但似乎没有一个问题。我在这里缺少什么?

非常感谢任何帮助。

更新 - 开始

如果有人想玩游戏并找到原因

,请添加一个说明问题的傻瓜

https://plnkr.co/edit/FW49ztzsg5uyXF7DtHDY?p=preview

更新 - 结束

父组件

import { Component, OnInit, ViewContainerRef, ViewChild, ComponentFactoryResolver, ViewChildren, QueryList } from '@angular/core';
import { ChildComponent } from './child/child.component';

@Component( {
    selector: 'parent-cmp',
    entryComponents: [ChildComponent],
    template: `
        <child-cmp (onSomeSelectionChange)="onSomeSelectionChange($event)"></child-cmp>
        <div #childCompSection></div>
        <button (click)="save()">Save</button>
    `
} )
export class ParentComponent implements OnInit {

    @ViewChild( 'childCompSection', { read: ViewContainerRef } ) childCompSection: any;
    currentComponentHolder: any;
    @ViewChildren( ChildComponent ) childComponents: QueryList<ChildComponent>;

    constructor( private resolver: ComponentFactoryResolver ) { }

    ngOnInit() {
        // do some stuff to load initial view, nothing done using childComponents
    }

    onSomeSelectionChange( someValue ) {
        if ( someValue !== 3 ) {
            let componentFactory = this.resolver.resolveComponentFactory( ChildComponent );
            this.currentComponentHolder = this.childCompSection.createComponent( componentFactory );
            this.currentComponentHolder.instance.onSomeSelectionChange.subscribe( data => this.onSomeSelectionChange( data ) );
        }
    }

    // Need all child components (which includes dynamically generated components) here 
    save() {
        console.log(this.childComponents); // This just prints the child-cmp object that is already added not the ones that are dynamically added via ComponentFactoryResolver
    }

}

子组件

import { Component, Output, EventEmitter } from '@angular/core';
import { ChildComponent } from './child/child.component';

@Component( {
    selector: 'child-cmp',
    template: `
        <div>
            <select [(ngModel)]="selectedValue" (ngModelChange)="showNumberField($event)">
                <option value="0">Select...</option>
                <option value="1">Add...</option>
                <option value="2">Subtract...</option>
                <option selected value="3">End</option>
            </select>
            <div *ngIf="showField">
                <label>Number:</label><input type="number" [(ngModel)]="numValue">
            </div>
        </div>
    `
} )
export class ChildComponent {

    @Output() onSomeSelectionChange = new EventEmitter<Lookup>();
    selectedValue: number;
    numValue: number;
    showField: boolean = false;

    showNumberField(value) {
        if(value !== 3) {
            showField = true;
        }
        this.onSomeSelectionChange.emit( value ); // This does work fine and another instance of the child component is generated from parent component but QueryList in parent component does not get updated
    }
}

5 个答案:

答案 0 :(得分:2)

毕竟您的子组件# main.py from readVision import vision v = vision("path/vision.csv") print type(v) pandas.core.frame.DataFrame 方法将执行

ngAfterViewInit

因此您无法在export class ParentComponent implements OnInit, AfterViewInit { ngAfterViewInit(){ ///////////////// your operations }

中执行操作

答案 1 :(得分:2)

来自Angular chat forum的确认@ ViewChildren / @ ViewChild只能读取视图中已有的元素。由于动态组件在视图初始化期间不可用,因此我必须维护所有这些动态生成的组件的列表以跟踪它们。

答案 2 :(得分:2)

我遇到了同样的问题,发现您可以订阅查询列表更改,如果添加或删除元素,将会触发更改。希望能帮助其他人下线...

@ViewChildren(MyComponent) myComp: QueryList<MyComponent>;

ngAfterViewInit() {
    this.myComp.changes
            .subscribe((queryChanges) => {
                // Do something
            });
}

答案 3 :(得分:0)

我认为此Angular bug仍未修复。我不得不手动删除该元素,如下所示,类似地,它也可以手动更新。

@ViewChildren('configElement') public configValues: QueryList<ConfigValueComponent>; (Declaration)
this.configValues['_results'].splice(data.index, 1); (remove deleted element)

答案 4 :(得分:0)

@ViewChildren@ContentChildren会针对动态内容进行更新,但是要达到这一目的则很棘手。

问题如下:

  • @Component中,模型T[]更新
  • *ngFor个绑定更新
  • 即使QueryList发生了变化,
  • *ngFor仍保持不变

当您考虑时,这是有道理的。更新了*ngFor的CD运行已经通过。为了解决这个问题,您将需要再次制作Angular run CD。让我们添加一些步骤:

  • @Component中,模型T[]更新
  • @Component中,我们运行this.changeDetectorRef.markForCheck();将视图标记为肮脏
  • @Component中,我们运行this.zone.run(() => Promise.resolve());来勾选应用程序
  • *ngFor个绑定更新
  • 即使QueryList发生了变化,
  • *ngFor仍保持不变
  • Promise触发,第二次CD运行开始
  • 由于视图被标记为脏,因此QueryList更新了