【发布时间】:2020-08-31 16:02:22
【问题描述】:
我需要在没有框架的情况下编写两个 Angular 组件(手风琴 > 扩展面板)。我正在使用 ContentChild 来支持手风琴组件中包含的所有扩展面板。我想要做的是解析组件以检查它们的状态并从父容器中切换更新属性的值。
现在我在每个扩展面板中都有一个事件发射器,如果每个实例是打开或关闭的,它就会发出。
我需要跟踪每个 ChildContent state$ 并在每个检查后设置一个默认值(如果有),如果内部子实例被切换则跟踪任何更改,它应该将事件发送到父手风琴,以便它可以确定展开时要跟踪的索引。
我希望遍历 ChildContent 的每个实例并检查是否已打开任何实例,如果没有通过更改其状态打开第一个实例。在包装器中,我订阅每个面板并在循环中检查它们是否打开,如果没有打开,我希望展开第一个子面板。
当一个子面板展开时,我希望收缩除选定的子手风琴之外的其余子手风琴。
@Component({
selector: 'app-accordion',
templateUrl: './accordion.component.html'
})
export class AccordionComponent extends Destroyable implements AfterContentInit, OnDestroy {
@ContentChildren(ExpansionPanelComponent) expansionPanels: QueryList<ExpansionPanelComponent>;
...
这是内部子组件(扩展面板)
@Component({
selector: 'app-expansion-panel',
templateUrl: './expansion-panel.component.html'
})
export class ExpansionPanelComponent extends Destroyable implements OnInit, OnDestroy {
@Input() title!: string;
@Input() content!: string | string[];
/**
* The default for this input would be overwritten if the optional
* @Input() defaultState is provided during initialization.
* The defaultState override occurs inside of this.setDefaultState();
*/
@Input() defaultState: ExpansionPanelState = ExpansionPanelState.Collapsed;
@Input() footerContent?: string;
@Input() headerIcon?: IconName;
@Input() toggleButton?: ViewChild;
@Output() expanded$ = new EventEmitter<ExpansionPanelState>();
setState(expansionPanelState: ExpansionPanelState): void {
if (this.expanded$.observers.length > 0) {
this.expanded$.emit(expansionPanelState);
}
this.state$.next(expansionPanelState);
}
这里是容器组件(Accordion)模板,让您了解这应该是多么简单......
{{ 手风琴标题 }} {{ 手风琴字幕 }} /** ' * ContentChildren 将所有子组件呈现为展开状态,否 *不管我尝试过什么方法。我已确认该活动正在进行中 * 发送给父母,我也看到各个面板在我切换时 * 预计。得不到的是所描述的动态互斥行为 * 上面当用户展开一个面板时,它应该收缩其余部分。什么时候 * 面板单独使用,应该可以配置使用 * 独立于手风琴(现在很好)。如何映射/变异/设置 * 一旦我动态调整了孩子的值 * 组件状态? */问题在于,由于 ContentChild 包含在 a 中,因此一旦使用 ContentChild.prototype.map 方法,我就无法将映射版本重新分配给它,并且我想避免手动渲染组件的开销。
扩展面板工作正常,经过单元测试,一切正常。手风琴和扩展面板过滤和选择让我很难过。我不断收到无限循环等,我知道我必须有一种更简单的方法来动态更新 ContentChildren,任何人都可以引导我朝着正确的方向前进吗?
那么我如何从其包装组件中查询并更新 ContentChildren?
https://codesandbox.io/s/silly-river-jsbuu?file=/src/app/accordion/accordion.component.ts:0-2156
映射将在 AccordionComponent.ngAfterContentInit() 中进行,但我无法使用下面链接的交互式沙箱中的方法使其工作。
谢谢!
【问题讨论】:
-
你能在 stackblitz.com 上做一个演示吗?
-
是的,抱歉花了这么长时间我不得不简化一些事情codesandbox.io/s/silly-river-jsbuu?file=/src/app/accordion/…
-
我快速浏览了您的代码框。我认为你的 observables 设置还有改进的空间。但就像现在一样,如果您将手风琴组件的整个
ngAfterViewInit逻辑替换为this.expansionPanels.forEach(element => element.state$.next('collapsed');});,则所有面板最初都将折叠。 -
但我对 observables 设置的评论是,您将命令式方法与反应式方法混合在一起。选择一个并坚持下去。例如,您处理切换、获取主题作为参数以及解构它以获取值的方式对我来说并不合适:D。
标签: angular typescript angular2-ngcontent