Reputation: 1616
I have one parent
and 2 child components
like below
Parent-component.html
<child-component-1 [id]="id"></child-component-1>
<child-component-2></child-component-2>
child-component-1
has ngb-carousel component to show warnings and alerts to user.And this component is being created due to id
and its result
comes from API
like below
child-component-1.ts
alertFound;
this.service.checkAlert(id).subscribe((result:any)=>{
if(result.alertFound){
this.alertFound=true;
}
})
child-component-1.html
<ngb-carousel #carousel *ngIf="alertFound" class="carousel-alert" [interval]="false" [wrap]="false"
[showNavigationIndicators]="false">
........................(Things go on here after result comes)
</ngb-carousel>
In child-component-2
,I need to get the height
of this ngb-alert
to make dynamic height calculation on the screen.If it exists,I need to subtract it from window.innerheight
.
child-component-2.html
<div [style.height.px]="actualHeight"></div>
child-component-2.ts
this.actualHeight = window.innerHeight - 269;
Interestingly on child-component-1
when I tried to track it like below
@ViewChild('carousel', {read: ElementRef, static:false}) elementView: ElementRef;
ngAfterViewInit(){
console.log(this.elementView.nativeElement.offSetHeight);
//throws height of created carousel first,then if next id doesnt have alert still shows me the height which previously created
}
it shows previously created carousel
height even though result is false
and carousel
couldn't be created at that moment.I guess this happening because result sometimes arrives very late,sometimes fast.To listen detections on viewchild
I found something like below which works exactly as I wanted and solved emitting non-created alert height problem
@ViewChildren('carousel', {read: ViewContainerRef}) viewContainerRefs;
ngAfterViewInit() {
this.viewContainerRefs.changes.subscribe(item => {
if (item) {
console.log(item._results[0]._lContainer[0][0].offsetHeight);
}
})
}
Now problem is sending this height to child-component-2 where I should calculate height dynamically.
I considered 2 options.First one is creating subject on a service,emitting height to it from child-component-1
and listening it on child-component-2
like below
service.ts
alertActive = new BehaviorSubject(0);
setAlertHeight(value:number){
this.alertActive.next(value)
}
child-component-1
ngAfterViewInit() {
this.viewContainerRefs.changes.subscribe(item => {
if (item) {
this.service.setAlertHeight(item._results[0]._lContainer[0][0].offsetHeight);
}
})
}
child-component-2
this.alertActive.subscribe(value=>{
if(value){
this.actualHeight-=value;
}
})
On the example above,it causes the problem to bring height,even though alert not created.I logged subscribes on console from child-2
and child-1
and noticed that child-2
prints to console even though child1 didnt emit anything to it.
So I considered another option to send height from child-1 to parent via @Output and then via Input to child2 like below
child-1
@Output() transferHeight: EventEmitter<any> = new EventEmitter();
this.viewContainerRefs.changes.subscribe(item => {
if (item) {
this.transferHeight.emit(item._results[0]._lContainer[0][0].offsetHeight);
}
})
parent
<child-component-1 (transferHeight)="transferedHeight($event)" [id]="id"></child-component-1>
<child-component-2 [transferredHeight]="transferredHeight"></child-component-2>
alertHeight;
transferedHeight(comingHeight){
this.alertHeight=comingHeight;
}
child-2
@Input() transferredHeight;
ngOnInit(){
this.actualHeight-=this.transferredHeight;
}
this one really handles the problem that I mentioned previously.But if carousel created after child-2 created due to late network resut ,it returns undefined.Therefore I tried to use ngOnChanges to listen changes on the Input variable like below
ngOnChanges(changes: SimpleChanges) {
console.log(changes.transferredHeight.currentValue)
}
this console.log doesnt print anything although I implemented onChanges.So Im waiting for your help.If there's a way to listen viewContainerRefs.changes
of child-1 from child-2 would be the best case
Upvotes: 3
Views: 3453
Reputation: 2324
The reason this attempt:
alertActive = new BehaviorSubject(0);
setAlertHeight(value:number){
this.alertActive.next(value)
}
triggered the height to be set even when the alert was not active was because you used a BehaviorSubject
rather than just a Subject
. A BehaviorSubject provides an initial value (0
in your case), so immediately upon subscription the subscriber will receive that initial value (or the latest emitted value).
So you were on the right track, you just needed to use a regular Subject
instead because then subscribers won't receive any values until the Subject
emits.
So your service could look like this:
private _alertActive = new Subject<number>();
get alertActive(): Observable<number> {
return this._alertActive.asObservable();
}
setAlertHeight(height: number) {
this._alertActive.next(height);
}
(Note that the Subject
is a private member but is exposed via a getter as an Observable. In general, subscribers shouldn't have access to the raw Subject unless they'll also be emitting values from it.)
And your child-2 component would subscribe to it like so:
this.service.alertActive.subscribe((height: number) => {
this.actualHeight -= height;
});
You were also on the right track using ViewChildren
and subscribing to its changes
, but it might be simpler if you reference the ElementRef
s rather than the ViewContainerRef
s. That would look like this:
import { NgbCarousel } from "@ng-bootstrap/ng-bootstrap";
@ViewChildren(NgbCarousel, { read: ElementRef }) carousels: QueryList<ElementRef>;
ngAfterViewInit() {
this.carousels.changes.subscribe((els: QueryList<ElementRef>) => {
const height = els.first ? els.first.nativeElement.offsetHeight : 0;
this.service.setAlertHeight(height);
});
}
Here's a StackBlitz showing it working with this approach.
Upvotes: 2