diff --git a/packages/datx/src/View.ts b/packages/datx/src/View.ts index fd73de50e..348d42765 100644 --- a/packages/datx/src/View.ts +++ b/packages/datx/src/View.ts @@ -3,25 +3,27 @@ import { action, computed, intercept, IObservableArray, observable, reaction } f import { SORTED_NO_WRITE, UNIQUE_MODEL } from './errors'; import { error } from './helpers/format'; +import { isPropertySelectorFn } from './helpers/view'; import { getModelId, getModelType } from './helpers/model/utils'; import { IIdentifier } from './interfaces/IIdentifier'; import { IModelConstructor } from './interfaces/IModelConstructor'; import { IRawView } from './interfaces/IRawView'; import { IType } from './interfaces/IType'; import { TChange } from './interfaces/TChange'; +import { ISortMethod } from './interfaces/IView'; import { PureCollection } from './PureCollection'; import { PureModel } from './PureModel'; export class View { public readonly modelType: IType; - @observable public sortMethod?: string|((item: T) => any); + @observable public sortMethod?: ISortMethod; private readonly __models: IObservableArray = observable.array([]); constructor( modelType: IModelConstructor|IType, protected __collection: PureCollection, - sortMethod?: string|((item: T) => any), + sortMethod?: ISortMethod, models: Array = [], public unique: boolean = false, ) { @@ -55,10 +57,14 @@ export class View { .filter(Boolean) as Array; if (this.sortMethod) { - const sortFn = typeof this.sortMethod === 'string' - ? (item) => item[this.sortMethod as 'string'] - : this.sortMethod; - list.sort((a: T, b: T) => sortFn(a) - sortFn(b)); + const sortFn = + typeof this.sortMethod === 'string' ? (item) => item[this.sortMethod as 'string'] : this.sortMethod; + + if (isPropertySelectorFn(sortFn)) { + list.sort((a: T, b: T) => sortFn(a) - sortFn(b)); + } else { + list.sort(sortFn); + } } const instances = observable.array(list, { deep: false }); diff --git a/packages/datx/src/helpers/view.ts b/packages/datx/src/helpers/view.ts new file mode 100644 index 000000000..ccf5944aa --- /dev/null +++ b/packages/datx/src/helpers/view.ts @@ -0,0 +1,6 @@ +import { ICompareFn, IPropertySelectorFn } from "../interfaces/IView"; + + +export function isPropertySelectorFn(fn: IPropertySelectorFn | ICompareFn): fn is IPropertySelectorFn { + return fn.length === 1; +} diff --git a/packages/datx/src/index.ts b/packages/datx/src/index.ts index a4f5107b1..b57d22b05 100644 --- a/packages/datx/src/index.ts +++ b/packages/datx/src/index.ts @@ -47,6 +47,7 @@ export { IReferenceOptions } from './interfaces/IReferenceOptions'; export { IViewConstructor } from './interfaces/IViewConstructor'; export { IActionsMixin } from './interfaces/IActionsMixin'; export { IMetaMixin } from './interfaces/IMetaMixin'; +export { ISortMethod } from './interfaces/IView'; export { PatchType } from './enums/PatchType'; export { ReferenceType } from './enums/ReferenceType'; diff --git a/packages/datx/src/interfaces/IView.ts b/packages/datx/src/interfaces/IView.ts new file mode 100644 index 000000000..03cb4ccdf --- /dev/null +++ b/packages/datx/src/interfaces/IView.ts @@ -0,0 +1,3 @@ +export type IPropertySelectorFn = (item: T) => any; +export type ICompareFn = (a: T, b: T) => number; +export type ISortMethod = string | IPropertySelectorFn | ICompareFn; diff --git a/packages/datx/test/view.ts b/packages/datx/test/view.ts index 3593fab0a..8a3e0b3af 100644 --- a/packages/datx/test/view.ts +++ b/packages/datx/test/view.ts @@ -13,7 +13,7 @@ import { view, } from '../src'; -describe('Model', () => { +describe('view', () => { it('should init a view', () => { const collection = new Collection(); const viewInstance = new View('foo', collection); @@ -198,6 +198,42 @@ describe('Model', () => { expect(item2b && item2b.key).toBe(2); }); + it('should be able to sort with compare function and non-unique props', () => { + class Foo extends Model { + public static type = 'foo'; + + @prop public notUnique!: number; + @prop public unique!: number; + } + class AppCollection extends Collection { + public static types = [Foo]; + } + + const collection = new AppCollection(); + const foos = collection.add([{ notUnique: 2, unique: 3 }, { notUnique: 2, unique: 1 }, { notUnique: 1, unique: 2 }], Foo); + + const compareFn = (a: Foo, b: Foo): number => { + if (a.notUnique === b.notUnique){ + return a.unique < b.unique ? -1 : 1 + } else { + return a.notUnique < b.notUnique ? -1 : 1 + } + } + + const viewInstance = new View(Foo, collection, compareFn, foos); + + expect(viewInstance.length).toBe(3); + const item0a = viewInstance.list[0]; + const item1a = viewInstance.list[1]; + const item2a = viewInstance.list[2]; + expect(item0a && item0a.notUnique).toBe(1); + expect(item0a && item0a.unique).toBe(2); + expect(item1a && item1a.notUnique).toBe(2); + expect(item1a && item1a.unique).toBe(1); + expect(item2a && item2a.notUnique).toBe(2); + expect(item2a && item2a.unique).toBe(3); + }); + it('should be able to remove models', () => { class Foo extends Model { public static type = 'foo';