Browse Source

feat: implement group features & improve feature-management.component

pull/5380/head
mehmet-erim 6 years ago
parent
commit
38f17eb7ed
  1. 108
      npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.html
  2. 116
      npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts

108
npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.html

@ -1,35 +1,107 @@
<abp-modal size="md" [(visible)]="visible" [busy]="modalBusy">
<abp-modal size="lg" [(visible)]="visible" [busy]="modalBusy">
<ng-template #abpHeader>
<h3>{{ 'AbpFeatureManagement::Features' | abpLocalization }}</h3>
</ng-template>
<ng-template #abpBody>
<form *ngIf="form" (ngSubmit)="save()" [formGroup]="form" validateOnSubmit>
<div
class="row my-3"
*ngFor="let feature of features$ | async; let i = index"
[ngSwitch]="feature.valueType.name"
>
<div class="mr-2">{{ feature.displayName }}</div>
<div *ngSwitchCase="'ToggleStringValueType'">
<input type="checkbox" name="feature.name" [formControlName]="i" />
</div>
<div *ngSwitchCase="'FreeTextStringValueType'">
<input type="text" name="feature.name" [formControlName]="i" />
</div>
<div class="row">
<div class="col-md-4">
<ul
ngbNav
#nav="ngbNav"
[(activeId)]="selectedGroup"
class="nav-pills"
orientation="vertical"
>
<li *ngFor="let groupName of groups" [ngbNavItem]="groupName">
<a ngbNavLink>{{ groupName }}</a>
<ng-template ngbNavContent>
<h4>{{ selectedGroup }}</h4>
<hr class="mt-2 mb-3" />
<div
class="row my-3"
*ngFor="let feature of features[groupName]; let i = index; trackBy: track.by('id')"
[ngStyle]="feature.style"
[ngSwitch]="feature.valueType?.name"
(keyup.enter)="save()"
>
<div *ngSwitchCase="valueTypes.ToggleStringValueType">
<div class="custom-checkbox custom-control">
<input
class="custom-control-input"
type="checkbox"
[id]="feature.name"
[(ngModel)]="feature.value"
/>
<label class="custom-control-label" [htmlFor]="feature.name">{{
feature.displayName
}}</label>
<ng-container
*ngTemplateOutlet="descTmp; context: { $implicit: feature.description }"
></ng-container>
</div>
</div>
<div *ngSwitchCase="valueTypes.FreeTextStringValueType">
<div class="form-group mb-0">
<label [htmlFor]="feature.name">{{ feature.displayName }}</label>
<input
class="form-control"
type="text"
[id]="feature.name"
[(ngModel)]="feature.value"
/>
<ng-container
*ngTemplateOutlet="descTmp; context: { $implicit: feature.description }"
></ng-container>
</div>
</div>
<div *ngSwitchCase="valueTypes.SelectionStringValueType">
<div class="form-group mb-0">
<label [htmlFor]="feature.name">{{ feature.displayName }}</label>
<select class="form-control" [id]="feature.name" [(ngModel)]="feature.value">
<option [ngValue]=""></option>
<option
*ngFor="
let prop of feature.valueType.properties | keyvalue;
trackBy: track.by('key')
"
[ngValue]="prop.value"
>
{{ prop.key }}</option
>
</select>
<ng-container
*ngTemplateOutlet="descTmp; context: { $implicit: feature.description }"
></ng-container>
</div>
</div>
<div *ngSwitchDefault>{{ feature.displayName }}</div>
</div>
</ng-template>
</li>
</ul>
</div>
<div *ngIf="!(features$ | async)?.length">
<ng-template #descTmp let-description>
<small *ngIf="description" class="form-text text-muted">{{ description }}</small>
</ng-template>
<div class="col-md-8"><div [ngbNavOutlet]="nav"></div></div>
<div class="mx-3" *ngIf="!groups.length">
{{ 'AbpFeatureManagement::NoFeatureFoundMessage' | abpLocalization }}
</div>
</form>
</div>
</ng-template>
<ng-template #abpFooter>
<ng-container *ngIf="(features$ | async)?.length">
<ng-container *ngIf="groups.length">
<button #abpClose type="button" class="btn btn-secondary">
{{ 'AbpFeatureManagement::Cancel' | abpLocalization }}
</button>
<abp-button iconClass="fa fa-check" [disabled]="form?.invalid || modalBusy" (click)="save()">
<abp-button iconClass="fa fa-check" [disabled]="modalBusy" (click)="save()">
{{ 'AbpFeatureManagement::Save' | abpLocalization }}
</abp-button>
</ng-container>

116
npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts

@ -1,11 +1,15 @@
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { GetFeatures, UpdateFeatures } from '../../actions';
import { TrackByService } from '@abp/ng.core';
import { LocaleDirection } from '@abp/ng.theme.shared';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { finalize } from 'rxjs/operators';
import { FeatureManagement } from '../../models/feature-management';
import { FeatureManagementState } from '../../states';
import { FormGroup, FormControl } from '@angular/forms';
import { pluck, finalize } from 'rxjs/operators';
import { FeatureDto, FeaturesService, UpdateFeatureDto } from '../../proxy/feature-management';
enum ValueTypes {
ToggleStringValueType = 'ToggleStringValueType',
FreeTextStringValueType = 'FreeTextStringValueType',
SelectionStringValueType = 'SelectionStringValueType',
}
@Component({
selector: 'abp-feature-management',
@ -22,6 +26,16 @@ export class FeatureManagementComponent
@Input()
providerName: string;
selectedGroup: string;
groups: string[] = [];
features: {
[group: string]: Array<FeatureDto & { style?: { [key: string]: number }; initialValue: any }>;
};
valueTypes = ValueTypes;
protected _visible;
@Input()
@ -30,22 +44,18 @@ export class FeatureManagementComponent
}
set visible(value: boolean) {
if (this._visible === value) return;
this._visible = value;
this.visibleChange.emit(value);
if (value) this.openModal();
}
@Output() readonly visibleChange = new EventEmitter<boolean>();
@Select(FeatureManagementState.getFeatures)
features$: Observable<FeatureManagement.Feature[]>;
modalBusy = false;
form: FormGroup;
constructor(private store: Store) {}
constructor(public readonly track: TrackByService, private service: FeaturesService) {}
openModal() {
if (!this.providerName) {
@ -56,52 +66,60 @@ export class FeatureManagementComponent
}
getFeatures() {
this.store
.dispatch(
new GetFeatures({
providerKey: this.providerKey,
providerName: this.providerName,
this.service.get(this.providerName, this.providerKey).subscribe(res => {
this.groups = res.groups.map(group => group.displayName);
this.selectedGroup = this.groups[0];
this.features = res.groups.reduce(
(acc, val) => ({
...acc,
[val.name]: mapFeatures(val.features, document.body.dir as LocaleDirection),
}),
)
.pipe(pluck('FeatureManagementState', 'features'))
.subscribe(features => {
this.buildForm(features);
});
}
buildForm(features) {
const formGroupObj = {};
for (let i = 0; i < features.length; i++) {
formGroupObj[i] = new FormControl(features[i].value === 'false' ? null : features[i].value);
}
this.form = new FormGroup(formGroupObj);
{},
);
});
}
save() {
if (this.modalBusy) return;
this.modalBusy = true;
const changedFeatures = [] as UpdateFeatureDto[];
let features = this.store.selectSnapshot(FeatureManagementState.getFeatures);
Object.keys(this.features).forEach(key => {
this.features[key].forEach(feature => {
if (feature.value !== feature.initialValue)
changedFeatures.push({ name: feature.name, value: feature.value });
});
});
features = features.map((feature, i) => ({
...feature,
value: this.form.value[i],
}));
this.store
.dispatch(
new UpdateFeatures({
providerKey: this.providerKey,
providerName: this.providerName,
features,
}),
)
if (!changedFeatures.length) {
this.visible = false;
return;
}
this.modalBusy = true;
this.service
.update(this.providerName, this.providerKey, { features: changedFeatures })
.pipe(finalize(() => (this.modalBusy = false)))
.subscribe(() => {
this.visible = false;
});
}
}
function mapFeatures(features: FeatureDto[], dir: LocaleDirection) {
const margin = `margin-${dir === 'rtl' ? 'right' : 'left'}.px`;
return features.map(feature => {
const value =
feature.valueType?.name === ValueTypes.ToggleStringValueType
? (feature.value || '').toLowerCase() === 'true'
: feature.value;
return {
...feature,
value,
initialValue: value,
style: { [margin]: feature.depth * 20 },
};
});
}

Loading…
Cancel
Save