我如何查看是否有一些兄弟投影内容?
How can i see if there is some sibling projected content?
我正在投影两个组件。
<app-form-field>
<component-one/>
<component-two/>
</app-form-field>
因此,如果我想知道组件一是否投影在内部 app-form-field
我会做:
@ContentChild(ComponentOne) componentOne;
ngAfterContentInit() {
if(this.componentOne) {
alert('it is projected');
} else {
alert('it is NOT projected');
}
}
但我需要检查我的 component-two
是否投影了组件一
我需要以某种方式检查组件二是否有同级内容投影 - component-one
.
我如何在 angular 中做到这一点?
可以检查是否有投影的兄弟组件,但要走的路可能很麻烦。
首先我们需要投影“子”组件的组件。
父组件
@Component({
selector: 'app-parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.css'],
providers: [ParentService]
})
export class ParentComponent implements AfterContentInit {
@ContentChildren(SelectorDirective) public refs: QueryList<SelectorDirective>;
constructor(private p: ParentService) {}
ngAfterContentInit(): void {
this.p.selectorDirectives.next(this.refs.toArray());
this.refs.changes.subscribe(x => {
this.p.selectorDirectives.next(this.refs.toArray());
});
}
ngOnInit() {}
}
注:
我们在组件级别注入服务 ParentService
,因此子组件和投影组件可以接收此实例。
对于不同类型的组件,我们需要一个统一的选择器(SelectorDirective
)传递给@ContentChildren
父服务:
@Injectable()
export class ParentService {
selectorDirectives: BehaviorSubject<
SelectorDirective[]
> = new BehaviorSubject([]);
constructor() {}
}
选择器指令:
@Directive({
selector: '[appSelector]'
})
export class SelectorDirective {
constructor(public componentRef: Basecomp) {}
}
注:
- 在构造函数中我们注入了
Basecomp
类型,所以这个指令附加到的每个元素都需要提供这个类型。
基础合成
export class Basecomp {}
注:
- 这仅用作注入令牌
现在我们需要为我们的统一令牌提供子组件的实例:
Comp1Component
@Component({
selector: 'app-comp1',
templateUrl: './comp1.component.html',
styleUrls: ['./comp1.component.css'],
providers: [
{ provide: Basecomp, useExisting: forwardRef(() => Comp1Component) }
]
})
export class Comp1Component implements OnInit {
constructor() {}
ngOnInit() {}
}
注:
- 我们还为该组件的实例提供 Basecomp-Token。如果 selectorDirective 附加到提供此令牌的组件,我们可以访问指令内部的组件实例。
现在回头看看ParentComponent的ngAfterContentInit:
- 我们 select 所有具有 SelectorDirective
的 ContentChildren
- 我们采用这些元素并在我们的 ParentService 中的主题上发出它们
- 我们还会查找 QueryList 的更改,如果发生更改,我们会在 ParentService 的 Subject 上发出它们
不,我们可以在 comp2 中注入 ParentService 并访问所有兄弟姐妹:
Comp2Component
@Component({
selector: 'app-comp2',
templateUrl: './comp2.component.html',
styleUrls: ['./comp2.component.css'],
providers: [
{ provide: Basecomp, useExisting: forwardRef(() => Comp2Component) }
]
})
export class Comp2Component implements OnInit {
constructor(private p: ParentService) {}
c1 = false;
ngOnInit() {
this.p.selectorDirectives.pipe().subscribe(x => {
this.c1 = !!x.find(a => {
return a.componentRef instanceof Comp1Component;
});
});
}
}
工作示例:https://stackblitz.com/edit/angular-ivy-t3qes6?file=src/app/comp2/comp2.component.ts
一个简单的解决方案是让父组件显式地将 component-one
引入 component-two
:
所以在你的 app-form-field
中你写了一些类似的东西:
@ContentChild(ComponentOne) componentOne;
@ContentChild(ComponentTwo) componentTwo;
ngAfterContentInit() {
this.componentTwo.componentOne = this.componentOne;
}
然后您可以在 component-two
中对此做出反应。
我知道这会在 app-form-field
component-one
和 component-two
之间造成紧密耦合,但在我看来它们似乎是完全耦合的,所以这种方式是可以的。
我正在投影两个组件。
<app-form-field>
<component-one/>
<component-two/>
</app-form-field>
因此,如果我想知道组件一是否投影在内部 app-form-field
我会做:
@ContentChild(ComponentOne) componentOne;
ngAfterContentInit() {
if(this.componentOne) {
alert('it is projected');
} else {
alert('it is NOT projected');
}
}
但我需要检查我的 component-two
是否投影了组件一
我需要以某种方式检查组件二是否有同级内容投影 - component-one
.
我如何在 angular 中做到这一点?
可以检查是否有投影的兄弟组件,但要走的路可能很麻烦。
首先我们需要投影“子”组件的组件。
父组件
@Component({
selector: 'app-parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.css'],
providers: [ParentService]
})
export class ParentComponent implements AfterContentInit {
@ContentChildren(SelectorDirective) public refs: QueryList<SelectorDirective>;
constructor(private p: ParentService) {}
ngAfterContentInit(): void {
this.p.selectorDirectives.next(this.refs.toArray());
this.refs.changes.subscribe(x => {
this.p.selectorDirectives.next(this.refs.toArray());
});
}
ngOnInit() {}
}
注:
我们在组件级别注入服务
ParentService
,因此子组件和投影组件可以接收此实例。对于不同类型的组件,我们需要一个统一的选择器(
SelectorDirective
)传递给@ContentChildren
父服务:
@Injectable()
export class ParentService {
selectorDirectives: BehaviorSubject<
SelectorDirective[]
> = new BehaviorSubject([]);
constructor() {}
}
选择器指令:
@Directive({
selector: '[appSelector]'
})
export class SelectorDirective {
constructor(public componentRef: Basecomp) {}
}
注:
- 在构造函数中我们注入了
Basecomp
类型,所以这个指令附加到的每个元素都需要提供这个类型。
基础合成
export class Basecomp {}
注:
- 这仅用作注入令牌
现在我们需要为我们的统一令牌提供子组件的实例:
Comp1Component
@Component({
selector: 'app-comp1',
templateUrl: './comp1.component.html',
styleUrls: ['./comp1.component.css'],
providers: [
{ provide: Basecomp, useExisting: forwardRef(() => Comp1Component) }
]
})
export class Comp1Component implements OnInit {
constructor() {}
ngOnInit() {}
}
注:
- 我们还为该组件的实例提供 Basecomp-Token。如果 selectorDirective 附加到提供此令牌的组件,我们可以访问指令内部的组件实例。
现在回头看看ParentComponent的ngAfterContentInit:
- 我们 select 所有具有 SelectorDirective 的 ContentChildren
- 我们采用这些元素并在我们的 ParentService 中的主题上发出它们
- 我们还会查找 QueryList 的更改,如果发生更改,我们会在 ParentService 的 Subject 上发出它们
不,我们可以在 comp2 中注入 ParentService 并访问所有兄弟姐妹:
Comp2Component
@Component({
selector: 'app-comp2',
templateUrl: './comp2.component.html',
styleUrls: ['./comp2.component.css'],
providers: [
{ provide: Basecomp, useExisting: forwardRef(() => Comp2Component) }
]
})
export class Comp2Component implements OnInit {
constructor(private p: ParentService) {}
c1 = false;
ngOnInit() {
this.p.selectorDirectives.pipe().subscribe(x => {
this.c1 = !!x.find(a => {
return a.componentRef instanceof Comp1Component;
});
});
}
}
工作示例:https://stackblitz.com/edit/angular-ivy-t3qes6?file=src/app/comp2/comp2.component.ts
一个简单的解决方案是让父组件显式地将 component-one
引入 component-two
:
所以在你的 app-form-field
中你写了一些类似的东西:
@ContentChild(ComponentOne) componentOne;
@ContentChild(ComponentTwo) componentTwo;
ngAfterContentInit() {
this.componentTwo.componentOne = this.componentOne;
}
然后您可以在 component-two
中对此做出反应。
我知道这会在 app-form-field
component-one
和 component-two
之间造成紧密耦合,但在我看来它们似乎是完全耦合的,所以这种方式是可以的。