使用TypeScript的Angular 5应用程序。当尝试实现组件之间的通信时,我遇到了称为循环依赖的问题。
有两个组件radio
和radio-group
:
<radio-group [(value)]='selected'>
<radio value='1'></radio>
<radio value='2'></radio>
<radio value='3'></radio>
</radio-group>
当用户选择和取消选择项目时,他们会相互沟通。
组件实现示例 RadioGroupComponent:
import { Component, forwardRef, Input, Optional, ContentChildren,
QueryList, EventEmitter, Output, ChangeDetectorRef, ChangeDetectionStrategy, AfterContentInit, OnChanges } from '@angular/core';
import { RadioGroup } from './radio-group.component';
@Component({
selector: 'radio',
styles: [`:host{cursor:pointer;}`],
template: `<div (click)='check()'>
<span *ngIf='!checked'>⚪️</span>
<span *ngIf='checked'></span>
<span>Click me. Value: {{value}} Checked: {{checked}}</span>
</div>`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class Radio {
@Input() value: any;
checked = false;
private _radioGroup: RadioGroup;
constructor(@Optional() radioGroup: RadioGroup, public cd: ChangeDetectorRef){
this._radioGroup = radioGroup;
}
check(){
this.checked = true;
if(this._radioGroup){
this._radioGroup.selected = this;
}
this.markForCheck();
}
markForCheck(){
this.cd.markForCheck();
}
}
RadioComponent:
import { Component, forwardRef, Input, Optional, ContentChildren, QueryList, EventEmitter, Output, ChangeDetectorRef, ChangeDetectionStrategy, AfterContentInit, OnChanges } from '@angular/core';
import { Radio } from './radio.component';
@Component({
selector: 'radio-group',
template: `<ng-content></ng-content>`,
})
export class RadioGroup implements AfterContentInit, OnChanges{
set selected (component:Radio){
this._selected = component;
this.valueChange.emit(component.value);
}
private _selected:Radio = null;
@Input() value:any;
@Output() valueChange = new EventEmitter();
@ContentChildren(forwardRef(() => Radio)) radioComponents: QueryList<Radio>;
ngAfterContentInit() { this.checkParentComponents();}
ngOnChanges(){ this.checkParentComponents();}
checkParentComponents():void{
this.radioComponents
&& this.radioComponents.forEach(item=>{
item.checked = item.value==this.value;
if(item.checked){ this._selected = item;}
item.markForCheck();
});
}
}
Working example with all declarations in one file (stackblitz.com)
Broken example with separated files (stackblitz.com)
如何通过循环依赖解决此问题并将所有组件和实现放入单独的文件中? 随着时间成分变得沉重,我如何将它们切成碎片?
答案 0 :(得分:2)
您不应该从RadioGroup
修改Radio
属性。子组件和父组件之间的通信应通过@Input
和@Output
进行。
从RadioGroup
构造函数中删除Radio
。相反,你可以做,
import {
Component,
Input,
EventEmitter,
Output,
ChangeDetectorRef,
ChangeDetectionStrategy
} from '@angular/core';
@Component({
selector: 'radio',
styles: [`:host{cursor:pointer;}`],
template: `
<div (click)='check()'>
<span *ngIf='!checked'>⚪️</span>
<span *ngIf='checked'></span>
<span>Click me. Value: {{value}} Checked: {{checked}}</span>
</div>`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class Radio {
@Input() value: any;
@Output() valueChange: EventEmitter<any> = new EventEmitter();
checked = false;
constructor(public cd: ChangeDetectorRef){
}
check(){
this.checked = true;
this.valueChange.emit(this.value);
this.markForCheck();
}
markForCheck(){
this.cd.markForCheck();
}
}
RadioGroup.component
@Component({
selector: 'radio-group',
template: `<ng-content></ng-content>`,
})
export class RadioGroup implements AfterContentInit, OnChanges, OnDestroy {
set selected(component: Radio) {
this._selected = component;
this.valueChange.emit(component.value);
}
private _selected: Radio = null;
@Input() value: any;
@Output() valueChange = new EventEmitter();
@ContentChildren(forwardRef(() => Radio)) radioComponents: QueryList<Radio>;
subscriptionList = [];
ngAfterContentInit() { this.checkParentComponents(); }
ngOnChanges() { this.checkParentComponents(); }
checkParentComponents(): void {
if (this.radioComponents) {
this.subscriptionList = this.radioComponents.map(item => {
item.checked = item.value === this.value;
if (item.checked) { this._selected = item; }
item.markForCheck();
// subscribe to each child "valueChange" event and return these subscriptions.
return item.valueChange.subscription(value => this.selected = value);
});
}
}
ngOnDestroy() {
// don't forget to unsubscribe.
if (this.subscriptionList && this.subscriptionList.length ) {
this.subscriptionList.forEach(sub => sub.unsubscribe());
}
}
}
答案 1 :(得分:0)
尝试使用 Radio 组件构造函数而不是
constructor(@Optional() radioGroup: RadioGroup, public cd: ChangeDetectorRef)
此代码
constructor(@Inject(forwardRef(() => RadioGroup)) radioGroup: RadioGroup, public cd: ChangeDetectorRef)
答案 2 :(得分:0)
当您需要在组件之间建立通信
并且 不能 使用 @Output 和 @Inputs ,请使用providers
。 DependencyInjection允许您在需要时覆盖构造函数类中的注入。
DependencyInjection (angular.io)
Avoid circular dependencies(angular.io)
radio-group.component.ts:
import { RadioGroupBase } from './radio-group-base';
@Component({
selector: 'radio-group',
...
==> providers: [{provide: RadioGroupBase, useExisting: RadioGroup}]
})
export class RadioGroup implements AfterContentInit, OnChanges{
...
}
radio.component.ts:
import { RadioGroupBase } from './radio-group-base';
@Component({
selector: 'radio',
...
})
export class Radio {
constructor(
==> @Optional() radioGroup: RadioGroupBase,
){
//variable radioGroup will be undefined when there is no provider.
}
radio-group-base.ts:
export class RadioGroupBase {
selected: any;
}
工作解决方案:
答案 3 :(得分:-1)
使用共享服务 https://angularfirebase.com/lessons/sharing-data-between-angular-components-four-methods/ 部分:&#34;不相关的组件:与服务共享数据&#34;