110889: Made the SearchLabelLoaderComponent extend AbstractComponentLoaderComponent

This commit is contained in:
Alexandre Vryghem
2024-02-17 00:01:54 +01:00
parent 386a1c92b7
commit 737195c223
4 changed files with 7 additions and 129 deletions

View File

@@ -1,16 +0,0 @@
import { Directive, ViewContainerRef } from '@angular/core';
/**
* Directive used as a hook to know where to inject the dynamic loaded component
*/
@Directive({
selector: '[dsSearchLabelLoader]'
})
export class SearchLabelLoaderDirective {
constructor(
public viewContainerRef: ViewContainerRef,
) {
}
}

View File

@@ -1 +0,0 @@
<ng-template dsSearchLabelLoader></ng-template>

View File

@@ -1,132 +1,30 @@
import { Component, ComponentRef, OnChanges, OnDestroy, OnInit, ViewChild, ViewContainerRef, SimpleChanges, Input } from '@angular/core'; import { Component, OnChanges, OnInit, Input } from '@angular/core';
import { Subscription } from 'rxjs';
import { GenericConstructor } from 'src/app/core/shared/generic-constructor'; import { GenericConstructor } from 'src/app/core/shared/generic-constructor';
import { hasValue, isNotEmpty } from 'src/app/shared/empty.util';
import { ThemeService } from '../../../theme-support/theme.service';
import { SearchLabelLoaderDirective } from './search-label-loader-directive.directive';
import { getSearchLabelByOperator } from './search-label-loader.decorator'; import { getSearchLabelByOperator } from './search-label-loader.decorator';
import { AppliedFilter } from '../../models/applied-filter.model'; import { AppliedFilter } from '../../models/applied-filter.model';
import { AbstractComponentLoaderComponent } from '../../../abstract-component-loader/abstract-component-loader.component';
@Component({ @Component({
selector: 'ds-search-label-loader', selector: 'ds-search-label-loader',
templateUrl: './search-label-loader.component.html', templateUrl: '../../../abstract-component-loader/abstract-component-loader.component.html',
}) })
export class SearchLabelLoaderComponent implements OnInit, OnChanges, OnDestroy { export class SearchLabelLoaderComponent extends AbstractComponentLoaderComponent<Component> implements OnInit, OnChanges {
@Input() inPlaceSearch: boolean; @Input() inPlaceSearch: boolean;
@Input() appliedFilter: AppliedFilter; @Input() appliedFilter: AppliedFilter;
/** protected inputNamesDependentForComponent: (keyof this & string)[] = [
* Directive to determine where the dynamic child component is located 'appliedFilter',
*/ ];
@ViewChild(SearchLabelLoaderDirective, { static: true }) componentDirective: SearchLabelLoaderDirective;
/**
* The reference to the dynamic component
*/
protected compRef: ComponentRef<Component>;
/**
* Array to track all subscriptions and unsubscribe them onDestroy
*/
protected subs: Subscription[] = [];
/**
* The @Input() that are used to find the matching component using {@link getComponent}. When the value of
* one of these @Input() change this loader needs to retrieve the best matching component again using the
* {@link getComponent} method.
*/
protected inputNamesDependentForComponent: (keyof this & string)[] = [];
/**
* The list of the @Input() names that should be passed down to the dynamically created components.
*/
protected inputNames: (keyof this & string)[] = [ protected inputNames: (keyof this & string)[] = [
'inPlaceSearch', 'inPlaceSearch',
'appliedFilter', 'appliedFilter',
]; ];
constructor(
protected themeService: ThemeService,
) {
}
/**
* Set up the dynamic child component
*/
ngOnInit(): void {
this.instantiateComponent();
}
/**
* Whenever the inputs change, update the inputs of the dynamic component
*/
ngOnChanges(changes: SimpleChanges): void {
if (hasValue(this.compRef)) {
if (this.inputNamesDependentForComponent.some((name: keyof this & string) => hasValue(changes[name]) && changes[name].previousValue !== changes[name].currentValue)) {
// Recreate the component when the @Input()s used by getComponent() aren't up-to-date anymore
this.destroyComponentInstance();
this.instantiateComponent();
} else {
this.connectInputsAndOutputs();
}
}
}
ngOnDestroy(): void {
this.subs
.filter((subscription: Subscription) => hasValue(subscription))
.forEach((subscription: Subscription) => subscription.unsubscribe());
this.destroyComponentInstance();
}
/**
* Creates the component and connects the @Input() & @Output() from the ThemedComponent to its child Component.
*/
public instantiateComponent(): void {
const component: GenericConstructor<Component> = this.getComponent();
const viewContainerRef: ViewContainerRef = this.componentDirective.viewContainerRef;
viewContainerRef.clear();
this.compRef = viewContainerRef.createComponent(
component, {
index: 0,
injector: undefined,
},
);
this.connectInputsAndOutputs();
}
/**
* Destroys the themed component and calls it's `ngOnDestroy`
*/
public destroyComponentInstance(): void {
if (hasValue(this.compRef)) {
this.compRef.destroy();
this.compRef = null;
}
}
/**
* Fetch the component depending on the item's entity type, metadata representation type and context
*/
public getComponent(): GenericConstructor<Component> { public getComponent(): GenericConstructor<Component> {
return getSearchLabelByOperator(this.appliedFilter.operator); return getSearchLabelByOperator(this.appliedFilter.operator);
} }
/**
* Connect the inputs and outputs of this component to the dynamic component,
* to ensure they're in sync
*/
public connectInputsAndOutputs(): void {
if (isNotEmpty(this.inputNames) && hasValue(this.compRef) && hasValue(this.compRef.instance)) {
this.inputNames.filter((name: string) => this[name] !== undefined).filter((name: string) => this[name] !== this.compRef.instance[name]).forEach((name: string) => {
this.compRef.instance[name] = this[name];
});
}
}
} }

View File

@@ -6,7 +6,6 @@ import { SearchFilterComponent } from './search-filters/search-filter/search-fil
import { SearchFacetFilterComponent } from './search-filters/search-filter/search-facet-filter/search-facet-filter.component'; import { SearchFacetFilterComponent } from './search-filters/search-filter/search-facet-filter/search-facet-filter.component';
import { SearchLabelsComponent } from './search-labels/search-labels.component'; import { SearchLabelsComponent } from './search-labels/search-labels.component';
import { SearchLabelLoaderComponent } from './search-labels/search-label-loader/search-label-loader.component'; import { SearchLabelLoaderComponent } from './search-labels/search-label-loader/search-label-loader.component';
import { SearchLabelLoaderDirective } from './search-labels/search-label-loader/search-label-loader-directive.directive';
import { SearchLabelComponent } from './search-labels/search-label/search-label.component'; import { SearchLabelComponent } from './search-labels/search-label/search-label.component';
import { SearchLabelRangeComponent } from './search-labels/search-label-range/search-label-range.component'; import { SearchLabelRangeComponent } from './search-labels/search-label-range/search-label-range.component';
import { SearchFacetFilterWrapperComponent } from './search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component'; import { SearchFacetFilterWrapperComponent } from './search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component';
@@ -86,7 +85,6 @@ export const MODELS = [
@NgModule({ @NgModule({
declarations: [ declarations: [
...COMPONENTS, ...COMPONENTS,
SearchLabelLoaderDirective,
], ],
imports: [ imports: [
CommonModule, CommonModule,
@@ -99,7 +97,6 @@ export const MODELS = [
], ],
exports: [ exports: [
...COMPONENTS, ...COMPONENTS,
SearchLabelLoaderDirective,
] ]
}) })
export class SearchModule { export class SearchModule {