mirror of https://github.com/abpframework/abp.git
7 changed files with 204 additions and 20 deletions
@ -0,0 +1,174 @@ |
|||
import { |
|||
Directive, |
|||
EmbeddedViewRef, |
|||
Input, |
|||
IterableChangeRecord, |
|||
IterableChanges, |
|||
IterableDiffer, |
|||
IterableDiffers, |
|||
OnChanges, |
|||
TemplateRef, |
|||
TrackByFunction, |
|||
ViewContainerRef, |
|||
} from '@angular/core'; |
|||
import compare from 'just-compare'; |
|||
|
|||
export type CompareFn<T = any> = (value: T, comparison: T) => boolean; |
|||
|
|||
class AbpForContext { |
|||
constructor(public $implicit: any, public index: number, public count: number, public list: any[]) {} |
|||
} |
|||
|
|||
class RecordView { |
|||
constructor(public record: IterableChangeRecord<any>, public view: EmbeddedViewRef<AbpForContext>) {} |
|||
} |
|||
|
|||
@Directive({ |
|||
selector: '[abpFor]', |
|||
}) |
|||
export class ForDirective implements OnChanges { |
|||
@Input('abpForOf') |
|||
items: any[]; |
|||
|
|||
@Input('abpForOrderBy') |
|||
orderBy: string; |
|||
|
|||
@Input('abpForOrderDir') |
|||
orderDir: 'ASC' | 'DESC'; |
|||
|
|||
@Input('abpForFilterBy') |
|||
filterBy: string; |
|||
|
|||
@Input('abpForFilterVal') |
|||
filterVal: any; |
|||
|
|||
@Input('abpForTrackBy') |
|||
trackBy; |
|||
|
|||
@Input('abpForCompareBy') |
|||
compareBy: CompareFn; |
|||
|
|||
@Input('abpForEmptyRef') |
|||
emptyRef: TemplateRef<any>; |
|||
|
|||
private differ: IterableDiffer<any>; |
|||
|
|||
private isShowEmptyRef: boolean; |
|||
|
|||
get compareFn(): CompareFn { |
|||
return this.compareBy || compare; |
|||
} |
|||
|
|||
get trackByFn(): TrackByFunction<any> { |
|||
return this.trackBy || ((index: number, item: any) => (item as any).id || index); |
|||
} |
|||
|
|||
constructor( |
|||
private tempRef: TemplateRef<AbpForContext>, |
|||
private vcRef: ViewContainerRef, |
|||
private differs: IterableDiffers, |
|||
) {} |
|||
|
|||
private iterateOverAppliedOperations(changes: IterableChanges<any>) { |
|||
const rw: RecordView[] = []; |
|||
|
|||
changes.forEachOperation((record: IterableChangeRecord<any>, previousIndex: number, currentIndex: number) => { |
|||
if (record.previousIndex == null) { |
|||
const view = this.vcRef.createEmbeddedView( |
|||
this.tempRef, |
|||
new AbpForContext(null, -1, -1, this.items), |
|||
currentIndex, |
|||
); |
|||
|
|||
rw.push(new RecordView(record, view)); |
|||
} else if (currentIndex == null) { |
|||
this.vcRef.remove(previousIndex); |
|||
} else { |
|||
const view = this.vcRef.get(previousIndex); |
|||
this.vcRef.move(view, currentIndex); |
|||
|
|||
rw.push(new RecordView(record, view as EmbeddedViewRef<AbpForContext>)); |
|||
} |
|||
}); |
|||
|
|||
for (let i = 0, l = rw.length; i < l; i++) { |
|||
rw[i].view.context.$implicit = rw[i].record.item; |
|||
} |
|||
} |
|||
|
|||
private iterateOverAttachedViews(changes: IterableChanges<any>) { |
|||
for (let i = 0, l = this.vcRef.length; i < l; i++) { |
|||
const viewRef = this.vcRef.get(i) as EmbeddedViewRef<AbpForContext>; |
|||
viewRef.context.index = i; |
|||
viewRef.context.count = l; |
|||
viewRef.context.list = this.items; |
|||
} |
|||
|
|||
changes.forEachIdentityChange((record: IterableChangeRecord<any>) => { |
|||
const viewRef = this.vcRef.get(record.currentIndex) as EmbeddedViewRef<AbpForContext>; |
|||
viewRef.context.$implicit = record.item; |
|||
}); |
|||
} |
|||
|
|||
private projectItems(items: any[]): void { |
|||
if (!items.length && this.emptyRef) { |
|||
this.vcRef.createEmbeddedView(this.emptyRef).rootNodes; |
|||
this.isShowEmptyRef = true; |
|||
|
|||
return; |
|||
} |
|||
|
|||
if (this.emptyRef && this.isShowEmptyRef) { |
|||
this.vcRef.clear(); |
|||
this.isShowEmptyRef = false; |
|||
} |
|||
|
|||
if (!this.differ && items) { |
|||
this.differ = this.differs.find(items).create(this.trackByFn); |
|||
} |
|||
|
|||
if (this.differ) { |
|||
const changes = this.differ.diff(items); |
|||
|
|||
if (changes) { |
|||
this.iterateOverAppliedOperations(changes); |
|||
this.iterateOverAttachedViews(changes); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private sortItems(items: any[]) { |
|||
if (this.orderBy) { |
|||
items.sort((a, b) => (a[this.orderBy] > b[this.orderBy] ? 1 : a[this.orderBy] < b[this.orderBy] ? -1 : 0)); |
|||
} else { |
|||
items.sort(); |
|||
} |
|||
} |
|||
|
|||
ngOnChanges() { |
|||
let items = [...this.items] as any[]; |
|||
if (!Array.isArray(items)) return; |
|||
|
|||
const compareFn = this.compareFn; |
|||
|
|||
if (typeof this.filterBy !== 'undefined') { |
|||
items = items.filter(item => compareFn(item[this.filterBy], this.filterVal)); |
|||
} |
|||
|
|||
switch (this.orderDir) { |
|||
case 'ASC': |
|||
this.sortItems(items); |
|||
this.projectItems(items); |
|||
break; |
|||
|
|||
case 'DESC': |
|||
this.sortItems(items); |
|||
items.reverse(); |
|||
this.projectItems(items); |
|||
break; |
|||
|
|||
default: |
|||
this.projectItems(items); |
|||
} |
|||
} |
|||
} |
|||
@ -1,5 +1,6 @@ |
|||
export * from './autofocus.directive'; |
|||
export * from './ellipsis.directive'; |
|||
export * from './for.directive'; |
|||
export * from './form-submit.directive'; |
|||
export * from './permission.directive'; |
|||
export * from './visibility.directive'; |
|||
|
|||
Loading…
Reference in new issue