Skip to content

Commit c1865af

Browse files
committed
refactor HasItems from mixin to class
1 parent 0908644 commit c1865af

File tree

2 files changed

+60
-80
lines changed

2 files changed

+60
-80
lines changed

src/HasItems.ts

Lines changed: 59 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -3,92 +3,62 @@ import LoadingStoreCollection from './LoadingStoreCollection'
33
import Resource from './interfaces/Resource'
44
import Collection from './interfaces/Collection'
55
import { Link } from './interfaces/StoreData'
6-
import ApiActions from './interfaces/ApiActions'
7-
import { InternalConfig } from './interfaces/Config'
6+
import StoreValue from './StoreValue'
87

9-
// Check Typescript Handbook fore more explanation of mixin pattern
10-
// https://www.typescriptlang.org/docs/handbook/mixins.html
11-
12-
// Now we use a generic version which can apply a constraint on
13-
// the class which this mixin is applied to
14-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
15-
type GConstructor<T> = new (...args: any[]) => T;
16-
17-
// Ensure property _storeData.items exist
18-
type HasStoreData = GConstructor<{ _storeData: { items: Array<Link> } }>;
19-
20-
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
21-
function HasItems<TBase extends HasStoreData> (Base: TBase, apiActions: ApiActions, config: InternalConfig) {
22-
/**
23-
* Filter out items that are marked as deleting (eager removal)
24-
*/
25-
function filterDeleting (array: Array<Resource>): Array<Resource> {
26-
return array.filter(entry => !entry._meta.deleting)
27-
}
28-
29-
/**
30-
* Replace each item in array with a proper StoreValue (or LoadingStoreValue)
31-
*/
32-
function replaceEntityReferences (array: Array<Link>): Array<Resource> {
33-
return array
34-
.filter(entry => isEntityReference(entry))
35-
.map(entry => apiActions.get(entry.href))
36-
}
8+
/**
9+
* Filter out items that are marked as deleting (eager removal)
10+
*/
11+
function filterDeleting (array: Array<Resource>): Array<Resource> {
12+
return array.filter(entry => !entry._meta.deleting)
13+
}
3714

15+
class HasItems extends StoreValue {
3816
/**
39-
* Returns true if any of the items within 'array' is not yet known to the API (meaning it has never been loaded)
40-
*/
41-
function containsUnknownEntityReference (array: Array<Link>): boolean {
42-
return array.some(entry => isEntityReference(entry) && apiActions.isUnknown(entry.href))
43-
}
44-
45-
const HasItems = class extends Base {
46-
/**
4717
* Get items excluding ones marked as 'deleting' (eager remove)
4818
* The items property should always be a getter, in order to make the call to mapArrayOfEntityReferences
4919
* lazy, since that potentially fetches a large number of entities from the API.
5020
*/
51-
public get items (): Array<Resource> {
52-
return filterDeleting(this._mapArrayOfEntityReferences(this._storeData.items))
53-
}
21+
public get items (): Array<Resource> {
22+
return filterDeleting(this._mapArrayOfEntityReferences(this._storeData.items))
23+
}
5424

55-
/**
25+
/**
5626
* Get all items including ones marked as 'deleting' (lazy remove)
5727
*/
58-
public get allItems (): Array<Resource> {
59-
return this._mapArrayOfEntityReferences(this._storeData.items)
60-
}
28+
public get allItems (): Array<Resource> {
29+
return this._mapArrayOfEntityReferences(this._storeData.items)
30+
}
6131

62-
/**
32+
/**
6333
* Returns a promise that resolves to the collection object, once all items have been loaded
6434
*/
65-
public $loadItems () :Promise<Collection> {
66-
return this._itemLoader(this._storeData.items)
67-
}
35+
public $loadItems () :Promise<Collection> {
36+
return this._itemLoader(this._storeData.items)
37+
}
6838

69-
/**
39+
/**
7040
* Returns a promise that resolves to the collection object, once all items have been loaded
7141
*/
72-
_itemLoader (array: Array<Link>) : Promise<Collection> {
73-
if (!containsUnknownEntityReference(array)) {
74-
return Promise.resolve(this as unknown as Collection) // we know that this object must be of type Collection
75-
}
42+
private _itemLoader (array: Array<Link>) : Promise<Collection> {
43+
if (!this._containsUnknownEntityReference(array)) {
44+
return Promise.resolve(this as unknown as Collection) // we know that this object must be of type Collection
45+
}
7646

77-
// eager loading of 'fetchAllUri' (e.g. parent for embedded collections)
78-
if (config.avoidNPlusOneRequests) {
79-
return apiActions.reload(this as unknown as Collection) as Promise<Collection> // we know that reload resolves to a type Collection
47+
// eager loading of 'fetchAllUri' (e.g. parent for embedded collections)
48+
if (this.config.avoidNPlusOneRequests) {
49+
return this.apiActions.reload(this as unknown as Collection) as Promise<Collection> // we know that reload resolves to a type Collection
8050

8151
// no eager loading: replace each reference (Link) with a StoreValue (Resource)
82-
} else {
83-
const arrayWithReplacedReferences = replaceEntityReferences(array)
52+
} else {
53+
const arrayWithReplacedReferences = this._replaceEntityReferences(array)
8454

85-
return Promise.all(
86-
arrayWithReplacedReferences.map(entry => entry._meta.load)
87-
).then(() => this as unknown as Collection) // we know that this object must be of type Collection
88-
}
55+
return Promise.all(
56+
arrayWithReplacedReferences.map(entry => entry._meta.load)
57+
).then(() => this as unknown as Collection) // we know that this object must be of type Collection
8958
}
59+
}
9060

91-
/**
61+
/**
9262
* Given an array, replaces any entity references in the array with the entity loaded from the Vuex store
9363
* (or from the API if necessary), and returns that as a new array. In case some of the entity references in
9464
* the array have not finished loading yet, returns a LoadingStoreCollection instead.
@@ -98,25 +68,38 @@ function HasItems<TBase extends HasStoreData> (Base: TBase, apiActions: ApiActio
9868
* @returns array the new array with replaced items, or a LoadingStoreCollection if any of the array
9969
* elements is still loading.
10070
*/
101-
_mapArrayOfEntityReferences (array: Array<Link>): Array<Resource> {
102-
if (!containsUnknownEntityReference(array)) {
103-
return replaceEntityReferences(array)
104-
}
71+
private _mapArrayOfEntityReferences (array: Array<Link>): Array<Resource> {
72+
if (!this._containsUnknownEntityReference(array)) {
73+
return this._replaceEntityReferences(array)
74+
}
10575

106-
const itemsLoaded = this._itemLoader(array).then(() => replaceEntityReferences(array))
76+
const itemsLoaded = this._itemLoader(array).then(() => this._replaceEntityReferences(array))
10777

108-
// eager loading of 'fetchAllUri' (e.g. parent for embedded collections)
109-
if (config.avoidNPlusOneRequests) {
110-
return LoadingStoreCollection.create(itemsLoaded)
78+
// eager loading of 'fetchAllUri' (e.g. parent for embedded collections)
79+
if (this.config.avoidNPlusOneRequests) {
80+
return LoadingStoreCollection.create(itemsLoaded)
11181

11282
// no eager loading: replace each reference (Link) with a StoreValue (Resource)
113-
} else {
114-
return LoadingStoreCollection.create(itemsLoaded, replaceEntityReferences(array))
115-
}
83+
} else {
84+
return LoadingStoreCollection.create(itemsLoaded, this._replaceEntityReferences(array))
11685
}
11786
}
11887

119-
return HasItems
88+
/**
89+
* Replace each item in array with a proper StoreValue (or LoadingStoreValue)
90+
*/
91+
private _replaceEntityReferences (array: Array<Link>): Array<Resource> {
92+
return array
93+
.filter(entry => isEntityReference(entry))
94+
.map(entry => this.apiActions.get(entry.href))
95+
}
96+
97+
/**
98+
* Returns true if any of the items within 'array' is not yet known to the API (meaning it has never been loaded)
99+
*/
100+
private _containsUnknownEntityReference (array: Array<Link>): boolean {
101+
return array.some(entry => isEntityReference(entry) && this.apiActions.isUnknown(entry.href))
102+
}
120103
}
121104

122105
export default HasItems

src/StoreValueCreator.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,7 @@ class StoreValueCreator {
6060
wrapData (data: StoreData): Resource {
6161
// Store data looks like a collection --> return Collection
6262
if (isCollection(data)) {
63-
// build Collection class = StoreValue + HasItems mixin
64-
const Collection = HasItems(StoreValue, this.apiActions, this.config)
65-
66-
return new Collection(data, this.apiActions, this, this.config) // these parameters are passed to StoreValue constructor
63+
return new HasItems(data, this.apiActions, this, this.config) // these parameters are passed to StoreValue constructor
6764

6865
// else Store Data looks like an entity --> return normal StoreValue
6966
} else {

0 commit comments

Comments
 (0)