Reputation: 1151
considering the following code as it's my custom ui-modal.component
import { CommonModule } from '@angular/common';
import { ChangeDetectionStrategy, Component, inject, input, output, signal, viewChild } from '@angular/core';
import { UIModalOptions } from './modal.model';
import { ModalService } from './modal.service';
@Component({
selector: 'ui-modal',
standalone: true,
imports: [CommonModule],
templateUrl: './ui-modal.component.html',
styleUrl: './ui-modal.component.css',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UiModalComponent {
private readonly modalService = inject(ModalService);
// Inputs
title = input<string>();
body = input<string>();
bodyClass = input<string>();
showHeaderCloseButton = input<boolean>(true);
showFooter = input<boolean>(true);
// Outputs
closed = output<void>();
// Template reference
readonly content = viewChild('modalContent');
useAsTemplate = input(true);
loadData = signal(false);
/**
* Opens the modal with optional configuration.
* @param options - Optional configuration for the modal.
*/
open(options?: UIModalOptions) {
return this.modalService.open(this.content(), options);
}
/**
* Closes the modal and emits the close event.
*/
close(): void {
this.closed.emit();
this.modalService.dismissAll();
}
}
<ng-template #modalContent>
<div class="modal-header d-flex justify-content-between">
<ng-content select="[modal-header]" />
@if (title()) {
<span class="modal-title">{{ title() }}</span>
}
@if (showHeaderCloseButton()) {
<button type="button" class="btn-close cup me-0 ms-auto p-0" aria-label="Close" (click)="close()">
<span aria-hidden="true"></span>
</button>
}
</div>
<div class="modal-body {{ bodyClass() }}">
@if (body()) {
<div class="text-justify">
<p>{{ body() }}</p>
</div>
}
<ng-content select="[modal-body]" />
</div>
@if (showFooter()) {
<div class="modal-footer">
<ng-content select="[modal-footer]" />
</div>
}
</ng-template>
And the following code example is showing how I am using it in other components
<ui-modal title="مدیریت فیلترها" bodyClass="p-0" [showFooter]="false">
<div modal-body class="h-100">
<filter-management class="overflow-hidden" (closeModal)="onFilterManagementClose()" />
</div>
</ui-modal>
As you can see in the html of the ui-modal component, I am using ng-template to defer content creation.
In fact, by the above structure, I expected the modal content (like filter-management component
) is only instantiated when I open the modal by calling the open method()
,
But It is not working.
Even though the filter-management
component is hidden initially, its constructor and Angular lifecycle hooks (like ngOnInit) are called immediately, before I actually open the modal with
Thanks for any help
Upvotes: 1
Views: 61
Reputation: 57986
That is how content projection
works, the content is initialized where you declare it, it is not initialized where you project it.
To solve this, wrap the content inside an ng-template
which is initialized where it us used.
<ui-modal title="مدیریت فیلترها" bodyClass="p-0" [showFooter]="false">
<ng-template #modalBody>
<div class="h-100">
<filter-management class="overflow-hidden" (closeModal)="onFilterManagementClose()" />
</div>
</ng-template>
</ui-modal>
Then you can use ngTemplateOutlet
to initialize it.
<ng-template #modalContent>
<div class="modal-header d-flex justify-content-between">
@if (modalHeader(); as modalHeader) {
<ng-container *ngTemplateOutlet="modalHeader" ></ng-container>
}
@if (title()) {
<span class="modal-title">{{ title() }}</span>
}
@if (showHeaderCloseButton()) {
<button type="button" class="btn-close cup me-0 ms-auto p-0" aria-label="Close" (click)="close()">
<span aria-hidden="true"></span>
</button>
}
</div>
<div class="modal-body {{ bodyClass() }}">
@if (body()) {
<div class="text-justify">
<p>{{ body() }}</p>
</div>
}
@if (modalBody(); as modalBody) {
<ng-container *ngTemplateOutlet="modalBody" ></ng-container>
}
</div>
@if (showFooter()) {
<div class="modal-footer">
@if (modalFooter(); as modalFooter) {
<ng-container *ngTemplateOutlet="modalFooter" ></ng-container>
}
</div>
}
</ng-template>
Then define the contentChild for them like:
readonly modalHeader = contentChild('modalHeader');
readonly modalBody = contentChild('modalBody');
readonly modalFooter = contentChild('modalFooter');
Upvotes: 1