Skip to content
This repository was archived by the owner on Nov 6, 2025. It is now read-only.

Commit 8e80ea3

Browse files
authored
Merge pull request #2161 from umbraco/v14/bugfix/remove-compositions
refactor composition logic
2 parents 8d6befc + b1e2170 commit 8e80ea3

File tree

3 files changed

+62
-44
lines changed

3 files changed

+62
-44
lines changed

src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { UmbContentTypeModel, UmbPropertyContainerTypes, UmbPropertyTypeContainerModel } from '../types.js';
22
import type { UmbContentTypeStructureManager } from './content-type-structure-manager.class.js';
33
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
4-
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
4+
import type { UmbController, UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
55
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
66

77
/**
@@ -17,6 +17,8 @@ export class UmbContentTypeContainerStructureHelper<T extends UmbContentTypeMode
1717

1818
#structure?: UmbContentTypeStructureManager<T>;
1919

20+
#containerObservers: Array<UmbController> = [];
21+
2022
// State containing the all containers defined in the data:
2123
#childContainers = new UmbArrayState<UmbPropertyTypeContainerModel>([], (x) => x.id);
2224
readonly containers = this.#childContainers.asObservable();
@@ -150,30 +152,31 @@ export class UmbContentTypeContainerStructureHelper<T extends UmbContentTypeMode
150152
this.#parentType,
151153
),
152154
(containers) => {
153-
// We want to remove hasProperties of groups that does not exist anymore.:
154-
// this.#removeHasPropertiesOfGroup()
155155
this.#hasProperties.setValue([]);
156156
this.#childContainers.setValue([]);
157+
this.#containerObservers.forEach((x) => x.destroy());
158+
this.#containerObservers = [];
157159

158160
containers.forEach((container) => {
159161
this.#observeHasPropertiesOf(container.id);
160162

161-
this.observe(
162-
this.#structure!.containersOfParentId(container.id, this.#childType!),
163-
(containers) => {
164-
// get the direct owner containers of this container id:
165-
this.#ownerChildContainers =
166-
this.#structure!.getOwnerContainers(this.#childType!, this.#containerId!) ?? [];
167-
// TODO: Maybe check for dif before setting it? Cause currently we are setting it every time one of the containers change. [NL]
168-
169-
// Remove existing containers that are not the parent of the new containers:
170-
this.#childContainers.filter(
171-
(x) => x.parent?.id !== container.id || containers.some((y) => y.id === x.id),
172-
);
173-
174-
this.#childContainers.append(containers);
175-
},
176-
'_observeGroupsOf_' + container.id,
163+
this.#containerObservers.push(
164+
this.observe(
165+
this.#structure!.containersOfParentId(container.id, this.#childType!),
166+
(containers) => {
167+
// get the direct owner containers of this container id: [NL]
168+
this.#ownerChildContainers =
169+
this.#structure!.getOwnerContainers(this.#childType!, this.#containerId!) ?? [];
170+
171+
// Remove existing containers that are not the parent of the new containers: [NL]
172+
this.#childContainers.filter(
173+
(x) => x.parent?.id !== container.id || containers.some((y) => y.id === x.id),
174+
);
175+
176+
this.#childContainers.append(containers);
177+
},
178+
'_observeGroupsOf_' + container.id,
179+
),
177180
);
178181
});
179182
},

src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export class UmbContentTypePropertyStructureHelper<T extends UmbContentTypeModel
2121

2222
#structure?: UmbContentTypeStructureManager<T>;
2323

24-
private _containerId?: string | null;
24+
#containerId?: string | null;
2525

2626
// State which holds all the properties of the current container, this is a composition of all properties from the containers that matches our target [NL]
2727
#propertyStructure = new UmbArrayState<UmbPropertyTypeModel>([], (x) => x.id);
@@ -59,12 +59,12 @@ export class UmbContentTypePropertyStructureHelper<T extends UmbContentTypeModel
5959
}
6060

6161
public setContainerId(value?: string | null) {
62-
if (this._containerId === value) return;
63-
this._containerId = value;
62+
if (this.#containerId === value) return;
63+
this.#containerId = value;
6464
this.#observeContainers();
6565
}
6666
public getContainerId() {
67-
return this._containerId;
67+
return this.#containerId;
6868
}
6969

7070
private _containerName?: string;
@@ -74,9 +74,9 @@ export class UmbContentTypePropertyStructureHelper<T extends UmbContentTypeModel
7474

7575
#containers?: Array<UmbPropertyTypeContainerModel>;
7676
#observeContainers() {
77-
if (!this.#structure || this._containerId === undefined) return;
77+
if (!this.#structure || this.#containerId === undefined) return;
7878

79-
if (this._containerId === null) {
79+
if (this.#containerId === null) {
8080
this.observe(
8181
this.#structure.propertyStructuresOf(null),
8282
(properties) => {
@@ -87,7 +87,7 @@ export class UmbContentTypePropertyStructureHelper<T extends UmbContentTypeModel
8787
this.removeUmbControllerByAlias('_observeContainers');
8888
} else {
8989
this.observe(
90-
this.#structure.containerById(this._containerId),
90+
this.#structure.containerById(this.#containerId),
9191
(container) => {
9292
if (container) {
9393
this._containerName = container.name ?? '';

src/packages/core/content-type/structure/content-type-structure-manager.class.ts

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,13 @@ export class UmbContentTypeStructureManager<
4242
readonly ownerContentType = this.#contentTypes.asObservablePart((x) =>
4343
x.find((y) => y.unique === this.#ownerContentTypeUnique),
4444
);
45-
46-
private readonly _contentTypeContainers = this.#contentTypes.asObservablePart((x) =>
47-
x.flatMap((x) => x.containers ?? []),
45+
readonly ownerContentTypeCompositions = this.#contentTypes.asObservablePart(
46+
(x) => x.find((y) => y.unique === this.#ownerContentTypeUnique)?.compositions,
4847
);
48+
49+
readonly #contentTypeContainers = this.#contentTypes.asObservablePart(() => {
50+
return this.#contentTypes.getValue().flatMap((x) => x.containers ?? []);
51+
});
4952
readonly contentTypeUniques = this.#contentTypes.asObservablePart((x) => x.map((y) => y.unique));
5053
readonly contentTypeAliases = this.#contentTypes.asObservablePart((x) => x.map((y) => y.alias));
5154

@@ -61,12 +64,12 @@ export class UmbContentTypeStructureManager<
6164
super(host);
6265
this.#repository = typeRepository;
6366

64-
this.observe(this.contentTypes, (contentTypes) => {
65-
contentTypes.forEach((contentType) => {
66-
this._loadContentTypeCompositions(contentType);
67-
});
67+
// Observe owner content type compositions, as we only allow one level of compositions at this moment. [NL]
68+
// But, we could support more, we would just need to flatMap all compositions and make sure the entries are unique and then base the observation on that. [NL]
69+
this.observe(this.ownerContentTypeCompositions, (ownerContentTypeCompositions) => {
70+
this._loadContentTypeCompositions(ownerContentTypeCompositions);
6871
});
69-
this.observe(this._contentTypeContainers, (contentTypeContainers) => {
72+
this.observe(this.#contentTypeContainers, (contentTypeContainers) => {
7073
this.#containers.setValue(contentTypeContainers);
7174
});
7275
}
@@ -136,8 +139,24 @@ export class UmbContentTypeStructureManager<
136139
this._observeContentType(data);
137140
}
138141

139-
private async _loadContentTypeCompositions(contentType: T) {
140-
contentType.compositions?.forEach((composition) => {
142+
private async _loadContentTypeCompositions(ownerContentTypeCompositions: T['compositions'] | undefined) {
143+
if (!ownerContentTypeCompositions) {
144+
// Owner content type was undefined, so we can not load compositions. But at this point we neither offload existing compositions, this is most likely not a case that needs to be handled.
145+
return;
146+
}
147+
148+
const ownerUnique = this.getOwnerContentTypeUnique();
149+
// Remove content types that does not exist as compositions anymore:
150+
this.#contentTypes.getValue().forEach((x) => {
151+
if (
152+
x.unique !== ownerUnique &&
153+
!ownerContentTypeCompositions.find((comp) => comp.contentType.unique === x.unique)
154+
) {
155+
this.#contentTypeObservers.find((y) => y.controllerAlias === 'observeContentType_' + x.unique)?.destroy();
156+
this.#contentTypes.removeOne(x.unique);
157+
}
158+
});
159+
ownerContentTypeCompositions.forEach((composition) => {
141160
this._ensureType(composition.contentType.unique);
142161
});
143162
}
@@ -164,23 +183,19 @@ export class UmbContentTypeStructureManager<
164183

165184
// Notice we do not store the content type in the store here, cause it will happen shortly after when the observations gets its first initial callback. [NL]
166185

167-
// Load inherited and composed types:
168-
//this._loadContentTypeCompositions(data);// Should not be necessary as this will be done when appended to the contentTypes state. [NL]
169-
170186
const ctrl = this.observe(
171187
// Then lets start observation of the content type:
172188
await this.#repository.byUnique(data.unique),
173189
(docType) => {
174190
if (docType) {
175-
// TODO: Handle if there was changes made to the owner document type in this context. [NL]
176-
/*
177-
possible easy solutions could be to notify user wether they want to update(Discard the changes to accept the new ones). [NL]
178-
*/
179191
this.#contentTypes.appendOne(docType);
192+
} else {
193+
// Remove the content type from the store, if it does not exist anymore.
194+
this.#contentTypes.removeOne(data.unique);
180195
}
181-
// TODO: Do we need to handle the undefined case? [NL]
182196
},
183197
'observeContentType_' + data.unique,
198+
// Controller Alias is used to stop observation when no longer needed. [NL]
184199
);
185200

186201
this.#contentTypeObservers.push(ctrl);

0 commit comments

Comments
 (0)