Browse Source

Merge pull request #4911 from abpframework/fix/4865

Improved the performance of extensibility system and fixed some problems
pull/4934/head
Erol Arkat 6 years ago
committed by GitHub
parent
commit
e5218173b0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      .github/workflows/angular.yml
  2. 6
      npm/ng-packs/package.json
  3. 3
      npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts
  4. 6
      npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts
  5. 40
      npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-form/extensible-form-prop.component.html
  6. 33
      npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-form/extensible-form-prop.component.ts
  7. 20
      npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-form/extensible-form.component.ts
  8. 10
      npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.html
  9. 27
      npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.ts
  10. 2
      npm/ng-packs/packages/theme-shared/package.json

11
.github/workflows/angular.yml

@ -3,15 +3,6 @@ on:
pull_request:
paths:
- 'npm/ng-packs/**'
branches:
#- master
- dev
push:
paths:
- 'npm/ng-packs/**'
branches:
#- master
- dev
jobs:
build-test-lint:
runs-on: ubuntu-18.04
@ -19,6 +10,6 @@ jobs:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: '10.x'
node-version: '12.x'
- run: yarn && yarn ci
working-directory: npm/ng-packs

6
npm/ng-packs/package.json

@ -16,7 +16,7 @@
"scripts:build": "cd scripts && yarn && yarn build",
"prepare:workspace": "yarn scripts:build --noInstall",
"ci": "yarn ng lint && yarn prepare:workspace && yarn ci:test && yarn ci:build",
"ci:test": "ng test --coverage=false",
"ci:test": "ng test --coverage=false --silent",
"ci:build": "cd scripts && yarn build:prod",
"lerna": "lerna",
"compile:ivy": "yarn ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points --tsconfig './tsconfig.prod.json' --source node_modules --async false",
@ -51,7 +51,7 @@
"@fortawesome/fontawesome-free": "^5.13.0",
"@ng-bootstrap/ng-bootstrap": "^6.1.0",
"@ngneat/spectator": "^5.11.0",
"@ngx-validate/core": "^0.0.9",
"@ngx-validate/core": "^0.0.10",
"@ngxs/devtools-plugin": "^3.6.2",
"@ngxs/logger-plugin": "^3.6.2",
"@ngxs/router-plugin": "^3.6.2",
@ -99,4 +99,4 @@
"resolutions": {
"@ngx-validate/core": "^0.0.8"
}
}
}

3
npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts

@ -77,7 +77,8 @@ describe('InitialUtils', () => {
const injectorSpy = jest.spyOn(injector, 'get');
const store = spectator.inject(Store);
store.selectSnapshot.andCallFake(selector => selector({ SessionState: { language: 'tr' } }));
injectorSpy.mockReturnValue(store);
injectorSpy.mockReturnValueOnce(store);
injectorSpy.mockReturnValueOnce({ cultureNameToLocaleFileNameMapping: {} });
expect(typeof localeInitializer(injector)).toBe('function');
expect(await localeInitializer(injector)()).toBe('resolved');
});

6
npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts

@ -1,3 +1,4 @@
import { CORE_OPTIONS } from '../tokens/options.token';
import { Router } from '@angular/router';
import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator/jest';
import { Actions, Store } from '@ngxs/store';
@ -13,7 +14,10 @@ describe('LocalizationService', () => {
service: LocalizationService,
entryComponents: [],
mocks: [Store, Router],
providers: [{ provide: Actions, useValue: new Subject() }],
providers: [
{ provide: Actions, useValue: new Subject() },
{ provide: CORE_OPTIONS, useValue: { cultureNameToLocaleFileNameMapping: {} } },
],
});
beforeEach(() => {

40
npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-form/extensible-form-prop.component.html

@ -1,15 +1,13 @@
<div class="form-group" [abpPermission]="prop.permission" [ngSwitch]="getComponent(prop)">
<ng-template ngSwitchCase="input">
<label [htmlFor]="prop.id"
>{{ prop.displayName | abpLocalization }} {{ getAsterisk(prop, data) }}</label
>
<label [htmlFor]="prop.id">{{ prop.displayName | abpLocalization }} {{ asterisk }}</label>
<input
[id]="prop.id"
[formControlName]="prop.name"
[autocomplete]="prop.autocomplete"
[type]="getType(prop)"
[abpDisabled]="prop.disabled(data)"
[readonly]="prop.readonly(data)"
[abpDisabled]="disabled"
[readonly]="readonly"
class="form-control"
/>
</ng-template>
@ -19,28 +17,26 @@
<input
[id]="prop.id"
[formControlName]="prop.name"
[abpDisabled]="prop.disabled(data)"
[abpDisabled]="disabled"
type="checkbox"
class="custom-control-input"
/>
<label [htmlFor]="prop.id" class="custom-control-label"
>{{ prop.displayName | abpLocalization }} {{ getAsterisk(prop, data) }}</label
>{{ prop.displayName | abpLocalization }} {{ asterisk }}</label
>
</div>
</ng-template>
<ng-template ngSwitchCase="select">
<label [htmlFor]="prop.id"
>{{ prop.displayName | abpLocalization }} {{ getAsterisk(prop, data) }}</label
>
<label [htmlFor]="prop.id">{{ prop.displayName | abpLocalization }} {{ asterisk }}</label>
<select
[id]="prop.id"
[formControlName]="prop.name"
[abpDisabled]="prop.disabled(data)"
[abpDisabled]="disabled"
class="custom-select form-control"
>
<option
*ngFor="let option of prop.options(data) | async; trackBy: track.by('value')"
*ngFor="let option of options$ | async; trackBy: track.by('value')"
[ngValue]="option.value"
>{{ option.key }}</option
>
@ -48,9 +44,7 @@
</ng-template>
<ng-template ngSwitchCase="date">
<label [htmlFor]="prop.id"
>{{ prop.displayName | abpLocalization }} {{ getAsterisk(prop, data) }}</label
>
<label [htmlFor]="prop.id">{{ prop.displayName | abpLocalization }} {{ asterisk }}</label>
<input
[id]="prop.id"
[formControlName]="prop.name"
@ -64,28 +58,22 @@
</ng-template>
<ng-template ngSwitchCase="time">
<label [htmlFor]="prop.id"
>{{ prop.displayName | abpLocalization }} {{ getAsterisk(prop, data) }}</label
>
<label [htmlFor]="prop.id">{{ prop.displayName | abpLocalization }} {{ asterisk }}</label>
<ngb-timepicker [formControlName]="prop.name"></ngb-timepicker>
</ng-template>
<ng-template ngSwitchCase="dateTime">
<label [htmlFor]="prop.id"
>{{ prop.displayName | abpLocalization }} {{ getAsterisk(prop, data) }}</label
>
<label [htmlFor]="prop.id">{{ prop.displayName | abpLocalization }} {{ asterisk }}</label>
<abp-date-time-picker [prop]="prop"></abp-date-time-picker>
</ng-template>
<ng-template ngSwitchCase="textarea">
<label [htmlFor]="prop.id"
>{{ prop.displayName | abpLocalization }} {{ getAsterisk(prop, data) }}</label
>
<label [htmlFor]="prop.id">{{ prop.displayName | abpLocalization }} {{ asterisk }}</label>
<textarea
[id]="prop.id"
[formControlName]="prop.name"
[abpDisabled]="prop.disabled(data)"
[readonly]="prop.readonly(data)"
[abpDisabled]="disabled"
[readonly]="readonly"
class="form-control"
></textarea>
</ng-template>

33
npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-form/extensible-form-prop.component.ts

@ -1,14 +1,17 @@
import { TrackByService } from '@abp/ng.core';
import { ABP, TrackByService } from '@abp/ng.core';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
Input,
Optional,
SimpleChanges,
SkipSelf,
OnChanges,
} from '@angular/core';
import { ControlContainer, Validators } from '@angular/forms';
import { ControlContainer, Validators, ValidatorFn } from '@angular/forms';
import { NgbDateAdapter, NgbTimeAdapter } from '@ng-bootstrap/ng-bootstrap';
import { Observable, of } from 'rxjs';
import { DateAdapter } from '../../adapters/date.adapter';
import { TimeAdapter } from '../../adapters/time.adapter';
import { ePropType } from '../../enums/props.enum';
@ -30,15 +33,23 @@ import { selfFactory } from '../../utils/factory.util';
{ provide: NgbTimeAdapter, useClass: TimeAdapter },
],
})
export class ExtensibleFormPropComponent {
export class ExtensibleFormPropComponent implements OnChanges {
@Input() data: PropData;
@Input() prop: FormProp;
options$: Observable<ABP.Option<any>[]> = of([]);
validators: ValidatorFn[] = [];
readonly: boolean;
disabled: boolean;
constructor(public readonly cdRef: ChangeDetectorRef, public readonly track: TrackByService) {}
getAsterisk(prop: FormProp, data: PropData): string {
return prop.validators(data).some(validator => validator === Validators.required) ? '*' : '';
get asterisk(): string {
return this.validators.some(validator => validator === Validators.required) ? '*' : '';
}
getComponent(prop: FormProp): string {
@ -77,4 +88,16 @@ export class ExtensibleFormPropComponent {
return 'hidden';
}
}
ngOnChanges({ prop }: SimpleChanges) {
const options = prop.currentValue.options;
const readonly = prop.currentValue.readonly;
const disabled = prop.currentValue.disabled;
const validators = prop.currentValue.validators;
if (options) this.options$ = options(this.data);
if (readonly) this.readonly = readonly(this.data);
if (disabled) this.disabled = disabled(this.data);
if (validators) this.validators = validators(this.data);
}
}

20
npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-form/extensible-form.component.ts

@ -1,20 +1,16 @@
import { TrackByService } from '@abp/ng.core';
import {
AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
Inject,
Input,
OnDestroy,
Optional,
QueryList,
SkipSelf,
ViewChildren,
} from '@angular/core';
import { ControlContainer, FormGroup } from '@angular/forms';
import { Subscription } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import { EXTRA_PROPERTIES_KEY } from '../../constants/extra-properties';
import { FormPropList } from '../../models/form-props';
import { ExtensionsService } from '../../services/extensions.service';
@ -35,7 +31,7 @@ import { ExtensibleFormPropComponent } from './extensible-form-prop.component';
},
],
})
export class ExtensibleFormComponent<R = any> implements AfterViewInit, OnDestroy {
export class ExtensibleFormComponent<R = any> {
@ViewChildren(ExtensibleFormPropComponent)
formProps: QueryList<ExtensibleFormPropComponent>;
@ -46,7 +42,6 @@ export class ExtensibleFormComponent<R = any> implements AfterViewInit, OnDestro
this.record = record;
}
private subscription = new Subscription();
extraPropertiesKey = EXTRA_PROPERTIES_KEY;
propList: FormPropList<R>;
record: R;
@ -66,17 +61,4 @@ export class ExtensibleFormComponent<R = any> implements AfterViewInit, OnDestro
private extensions: ExtensionsService,
@Inject(EXTENSIONS_IDENTIFIER) private identifier: string,
) {}
ngAfterViewInit() {
this.subscription.add(
this.form.statusChanges.pipe(debounceTime(0)).subscribe(() => {
this.formProps.forEach(prop => prop.cdRef.markForCheck());
this.cdRef.detectChanges();
}),
);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}

10
npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.html

@ -27,11 +27,11 @@
[sortable]="prop.sortable"
>
<ng-template let-row="row" let-i="index" ngx-datatable-cell-template>
<ng-container
*abpPropData="let data; fromList: propList; withRecord: row; atIndex: i"
[abpPermission]="prop.permission"
>
<div *ngIf="prop.visible(data)" [innerHTML]="getContent(prop, data) | async"></div>
<ng-container [abpPermission]="prop.permission">
<div
*ngIf="row['_' + prop.name].visible"
[innerHTML]="row['_' + prop.name].value | async"
></div>
</ng-container>
</ng-template>
</ngx-datatable-column>

27
npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.ts

@ -9,6 +9,11 @@ import {
LOCALE_ID,
TemplateRef,
TrackByFunction,
Type,
InjectionToken,
InjectFlags,
SimpleChanges,
OnChanges,
} from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@ -25,7 +30,7 @@ const DEFAULT_ACTIONS_COLUMN_WIDTH = 150;
templateUrl: './extensible-table.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ExtensibleTableComponent<R = any> {
export class ExtensibleTableComponent<R = any> implements OnChanges {
@Input() actionsText: string;
@Input() data: R[];
@Input() list: ListService;
@ -35,6 +40,8 @@ export class ExtensibleTableComponent<R = any> {
}
@Input() actionsTemplate: TemplateRef<any>;
getInjected: <T>(token: Type<T> | InjectionToken<T>, notFoundValue?: T, flags?: InjectFlags) => T;
readonly columnWidths: number[];
readonly propList: EntityPropList<R>;
@ -42,6 +49,8 @@ export class ExtensibleTableComponent<R = any> {
readonly trackByFn: TrackByFunction<EntityProp<R>> = (_, item) => item.name;
constructor(@Inject(LOCALE_ID) private locale: string, injector: Injector) {
// tslint:disable-next-line
this.getInjected = injector.get.bind(injector);
const extensions = injector.get(ExtensionsService);
const name = injector.get(EXTENSIONS_IDENTIFIER);
this.propList = extensions.entityProps.get(name).props;
@ -85,4 +94,20 @@ export class ExtensibleTableComponent<R = any> {
}),
);
}
ngOnChanges({ data }: SimpleChanges) {
if (!data?.currentValue) return;
this.data = data.currentValue.map((record, index) => {
this.propList.forEach(prop => {
const propData = { getInjected: this.getInjected, record, index } as any;
record[`_${prop.value.name}`] = {
visible: prop.value.visible(propData),
value: this.getContent(prop.value, propData),
};
});
return record;
});
}
}

2
npm/ng-packs/packages/theme-shared/package.json

@ -10,7 +10,7 @@
"@abp/ng.core": "~3.0.4",
"@fortawesome/fontawesome-free": "^5.13.1",
"@ng-bootstrap/ng-bootstrap": "^6.1.0",
"@ngx-validate/core": "^0.0.9",
"@ngx-validate/core": "^0.0.10",
"@swimlane/ngx-datatable": "^17.0.0",
"bootstrap": "^4.5.0",
"chart.js": "^2.9.3",

Loading…
Cancel
Save