Browse Source

feature(core): add abpFor directive

support to add layout type from routing module
pull/1741/head
mehmet-erim 7 years ago
parent
commit
2ac158d367
  1. 32
      npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts
  2. 3
      npm/ng-packs/packages/core/src/lib/core.module.ts
  3. 174
      npm/ng-packs/packages/core/src/lib/directives/for.directive.ts
  4. 1
      npm/ng-packs/packages/core/src/lib/directives/index.ts
  5. 1
      npm/ng-packs/packages/core/src/lib/enums/common.ts
  6. 2
      npm/ng-packs/packages/core/src/lib/models/common.ts
  7. 11
      templates/app/angular/src/app/app.module.ts

32
npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts

@ -1,12 +1,13 @@
import { Component, OnDestroy, Type } from '@angular/core';
import { NavigationEnd, Router, UrlSegment } from '@angular/router';
import { Component, Input, OnDestroy, Type } from '@angular/core';
import { ActivatedRoute, NavigationEnd, Router, UrlSegment } from '@angular/router';
import { Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { eLayoutType } from '../enums';
import { ABP, Config } from '../models';
import { ConfigState } from '../states';
import { takeUntilDestroy } from '../utils';
import snq from 'snq';
import { eLayoutType } from '../enums/common';
import { Config } from '../models/config';
import { ABP } from '../models/common';
import { ConfigState } from '../states/config.state';
import { takeUntilDestroy } from '../utils/rxjs-utils';
@Component({
selector: 'abp-dynamic-layout',
@ -23,16 +24,23 @@ export class DynamicLayoutComponent implements OnDestroy {
layout: Type<any>;
constructor(private router: Router, private store: Store) {
constructor(private router: Router, private route: ActivatedRoute, private store: Store) {
const {
requirements: { layouts },
routes,
} = this.store.selectSnapshot(ConfigState.getAll);
if ((this.route.snapshot.data || {}).layout) {
this.layout = layouts
.filter(l => !!l)
.find(l => snq(() => l.type.toLowerCase().indexOf(this.route.snapshot.data.layout), -1) > -1);
}
this.router.events.pipe(takeUntilDestroy(this)).subscribe(event => {
if (event instanceof NavigationEnd) {
const { segments } = this.router.parseUrl(event.url).root.children.primary;
const {
requirements: { layouts },
routes,
} = this.store.selectSnapshot(ConfigState.getAll);
const layout = findLayout(segments, routes);
const layout = (this.route.snapshot.data || {}).layout || findLayout(segments, routes);
this.layout = layouts.filter(l => !!l).find(l => snq(() => l.type.toLowerCase().indexOf(layout), -1) > -1);
}

3
npm/ng-packs/packages/core/src/lib/core.module.ts

@ -24,6 +24,7 @@ import { ProfileState } from './states/profile.state';
import { SessionState } from './states/session.state';
import { getInitialData, localeInitializer } from './utils/initial-utils';
import { ConfigPlugin, NGXS_CONFIG_PLUGIN_OPTIONS } from './plugins/config/config.plugin';
import { ForDirective } from './directives/for.directive';
@NgModule({
imports: [
@ -41,6 +42,7 @@ import { ConfigPlugin, NGXS_CONFIG_PLUGIN_OPTIONS } from './plugins/config/confi
DynamicLayoutComponent,
AutofocusDirective,
EllipsisDirective,
ForDirective,
FormSubmitDirective,
LocalizationPipe,
PermissionDirective,
@ -58,6 +60,7 @@ import { ConfigPlugin, NGXS_CONFIG_PLUGIN_OPTIONS } from './plugins/config/confi
DynamicLayoutComponent,
AutofocusDirective,
EllipsisDirective,
ForDirective,
FormSubmitDirective,
LocalizationPipe,
PermissionDirective,

174
npm/ng-packs/packages/core/src/lib/directives/for.directive.ts

@ -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
npm/ng-packs/packages/core/src/lib/directives/index.ts

@ -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';

1
npm/ng-packs/packages/core/src/lib/enums/common.ts

@ -2,4 +2,5 @@ export const enum eLayoutType {
account = 'account',
application = 'application',
empty = 'empty',
setting = 'setting',
}

2
npm/ng-packs/packages/core/src/lib/models/common.ts

@ -1,5 +1,5 @@
import { Config } from './config';
import { eLayoutType } from '../enums';
import { eLayoutType } from '../enums/common';
export namespace ABP {
export interface Root {

11
templates/app/angular/src/app/app.module.ts

@ -11,9 +11,9 @@ import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { SharedModule } from './shared/shared.module';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { RootAccountModule } from '@abp/ng.account';
import { RootIdentityModule } from '@abp/ng.identity';
import { RootTenantManagementModule } from '@abp/ng.tenant-management';
import { AccountProviders } from '@abp/ng.account';
import { IdentityProviders } from '@abp/ng.identity';
import { TenantManagementProviders } from '@abp/ng.tenant-management';
@NgModule({
declarations: [AppComponent],
@ -29,15 +29,12 @@ import { RootTenantManagementModule } from '@abp/ng.tenant-management';
layouts: LAYOUTS,
},
}),
RootAccountModule.forRoot({ redirectUrl: '/' }),
RootIdentityModule.forRoot(),
RootTenantManagementModule.forRoot(),
OAuthModule.forRoot(),
NgxsModule.forRoot([]),
NgxsReduxDevtoolsPluginModule.forRoot({ disabled: environment.production }),
],
providers: [],
providers: [...AccountProviders({ redirectUrl: '/' }), ...IdentityProviders(), ...TenantManagementProviders()],
bootstrap: [AppComponent],
})
export class AppModule {}

Loading…
Cancel
Save