[ADF-4586] - fix preselecting value for DropdownCloud (#4747)

* [ADF-4586] - fix preselecting value for DropdownCloud

* ADF-4586] - fix form complete for dropdown and tests

* [ADF-4586] - add FormRenderingService as provider

* [ADF-4586] - fix memory leaks

* [ADF-4586] - add type information

* [ADF-4586] - fix memory leak on dropdown

* [ADF-4589] - PR changes

* [ADF-4586] - fix unit tests

* [ADF-4586] - lint
This commit is contained in:
Silviu Popa
2019-05-30 20:13:41 +03:00
committed by Eugenio Romano
parent ce60915fd9
commit 892172150e
6 changed files with 114 additions and 18 deletions

View File

@@ -6,6 +6,7 @@
[id]="field.id" [id]="field.id"
[(ngModel)]="field.value" [(ngModel)]="field.value"
[disabled]="field.readOnly" [disabled]="field.readOnly"
[compareWith]="compareDropdownValues"
(ngModelChange)="onFieldChanged(field)"> (ngModelChange)="onFieldChanged(field)">
<mat-option *ngFor="let opt of field.options" <mat-option *ngFor="let opt of field.options"
[value]="getOptionValue(opt, field.value)" [value]="getOptionValue(opt, field.value)"

View File

@@ -159,7 +159,7 @@ describe('DropdownCloudWidgetComponent', () => {
}); });
})); }));
it('shoud map properties if restResponsePath is set', async(() => { it('shoud map properties if restResponsePath is set', (done) => {
widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id' }), { widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id' }), {
id: 'dropdown-id', id: 'dropdown-id',
name: 'date-name', name: 'date-name',
@@ -175,7 +175,8 @@ describe('DropdownCloudWidgetComponent', () => {
id: 1, id: 1,
path: { path: {
name: 'test1' name: 'test1'
} },
name: ''
}])); }]));
spyOn(widget, 'mapJsonData').and.returnValue([]); spyOn(widget, 'mapJsonData').and.returnValue([]);
@@ -183,8 +184,49 @@ describe('DropdownCloudWidgetComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
expect(widget.mapJsonData).toHaveBeenCalled(); expect(widget.mapJsonData).toHaveBeenCalled();
done();
}); });
})); });
it('shoud preselect dropdown widget value set as id ', (done) => {
widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id' }), {
id: 'dropdown-id',
name: 'date-name',
type: 'dropdown-cloud',
readOnly: 'false',
restUrl: 'fake-rest-url',
optionType: 'rest',
value: {
id: 'opt1',
name: 'defaul_value'
}
});
const dropdownSpy = spyOn(formCloudService, 'getDropDownJsonData').and.returnValue(of(<FormFieldOption[]> [
{
id: 'opt1',
name: 'default1_value'
},
{
id: 2,
name: 'default2_value'
}
]));
widget.ngOnInit();
fixture.detectChanges();
const dropDownElement: any = element.querySelector('#dropdown-id');
dropDownElement.click();
fixture.detectChanges();
fixture.whenStable().then(() => {
const optOne: any = fixture.debugElement.queryAll(By.css('[id="opt1"]'));
expect(dropdownSpy).toHaveBeenCalled();
expect(optOne[0].nativeElement.className).toBe('mat-option ng-star-inserted mat-active');
done();
});
});
}); });
}); });

View File

@@ -15,9 +15,11 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, OnInit, ViewEncapsulation } from '@angular/core'; import { Component, OnInit, ViewEncapsulation, OnDestroy } from '@angular/core';
import { baseHost, WidgetComponent, FormService, LogService, FormFieldOption } from '@alfresco/adf-core'; import { baseHost, WidgetComponent, FormService, LogService, FormFieldOption } from '@alfresco/adf-core';
import { FormCloudService } from '../../services/form-cloud.service'; import { FormCloudService } from '../../services/form-cloud.service';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
/* tslint:disable:component-selector */ /* tslint:disable:component-selector */
@@ -28,7 +30,9 @@ import { FormCloudService } from '../../services/form-cloud.service';
host: baseHost, host: baseHost,
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class DropdownCloudWidgetComponent extends WidgetComponent implements OnInit { export class DropdownCloudWidgetComponent extends WidgetComponent implements OnInit, OnDestroy {
protected onDestroy$ = new Subject<boolean>();
constructor(public formService: FormService, constructor(public formService: FormService,
private formCloudService: FormCloudService, private formCloudService: FormCloudService,
@@ -44,11 +48,13 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
getValuesFromRestApi() { getValuesFromRestApi() {
if (this.isValidRestType()) { if (this.isValidRestType()) {
this.formCloudService.getDropDownJsonData(this.field.restUrl).subscribe( (result: FormFieldOption[]) => { this.formCloudService.getDropDownJsonData(this.field.restUrl)
.pipe(takeUntil(this.onDestroy$))
.subscribe( (result: FormFieldOption[]) => {
if (this.field.restResponsePath) { if (this.field.restResponsePath) {
this.field.options = this.mapJsonData(result); this.field.options = this.mapJsonData(result);
} else { } else {
this.field.options = result; this.setOptionValues(result);
} }
}, },
(err) => this.handleError(err)); (err) => this.handleError(err));
@@ -67,7 +73,20 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
}); });
} }
getOptionValue(option: FormFieldOption, fieldValue: string): string { private setOptionValues(result: FormFieldOption[] ) {
this.field.options = result.map( (value: FormFieldOption) => {
return {
id: value.id,
name: value.name
};
});
}
compareDropdownValues(opt1: string, opt2: FormFieldOption): boolean {
return opt1 && opt2 && ( opt1 === opt2.id || opt1 === opt2.name);
}
getOptionValue(option: FormFieldOption, fieldValue: string): string {
let optionValue: string = ''; let optionValue: string = '';
if (option.id === 'empty' || option.name !== fieldValue) { if (option.id === 'empty' || option.name !== fieldValue) {
optionValue = option.id; optionValue = option.id;
@@ -89,4 +108,8 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
return this.field.type === 'readonly' ? true : false; return this.field.type === 'readonly' ? true : false;
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
} }

View File

@@ -20,7 +20,7 @@ import { By } from '@angular/platform-browser';
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Observable, of, throwError } from 'rxjs'; import { Observable, of, throwError } from 'rxjs';
import { FormFieldModel, FormFieldTypes, FormService, FormOutcomeEvent, FormOutcomeModel, LogService, WidgetVisibilityService, import { FormFieldModel, FormFieldTypes, FormService, FormOutcomeEvent, FormOutcomeModel, LogService, WidgetVisibilityService,
setupTestBed, AppConfigService } from '@alfresco/adf-core'; setupTestBed, AppConfigService, FormRenderingService } from '@alfresco/adf-core';
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module'; import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import { FormCloudService } from '../services/form-cloud.service'; import { FormCloudService } from '../services/form-cloud.service';
import { FormCloudComponent } from './form-cloud.component'; import { FormCloudComponent } from './form-cloud.component';
@@ -34,14 +34,18 @@ describe('FormCloudComponent', () => {
let formComponent: FormCloudComponent; let formComponent: FormCloudComponent;
let visibilityService: WidgetVisibilityService; let visibilityService: WidgetVisibilityService;
let logService: LogService; let logService: LogService;
let formRenderingService: FormRenderingService;
beforeEach(() => { beforeEach(() => {
logService = new LogService(null); logService = new LogService(null);
formRenderingService = TestBed.get(FormRenderingService);
visibilityService = new WidgetVisibilityService(null, logService); visibilityService = new WidgetVisibilityService(null, logService);
spyOn(visibilityService, 'refreshVisibility').and.stub(); spyOn(visibilityService, 'refreshVisibility').and.stub();
spyOn(formRenderingService, 'setComponentTypeResolver').and.returnValue(true);
formCloudService = new FormCloudService(null, new AppConfigService(null), logService); formCloudService = new FormCloudService(null, new AppConfigService(null), logService);
formService = new FormService(null, null, logService); formService = new FormService(null, null, logService);
formComponent = new FormCloudComponent(formCloudService, formService, null, visibilityService); formComponent = new FormCloudComponent(formCloudService, formService, null, formRenderingService, visibilityService);
}); });
it('should check form', () => { it('should check form', () => {

View File

@@ -17,21 +17,30 @@
import { import {
Component, EventEmitter, Input, OnChanges, Component, EventEmitter, Input, OnChanges,
Output, SimpleChanges Output, SimpleChanges, OnDestroy
} from '@angular/core'; } from '@angular/core';
import { Observable, of, forkJoin } from 'rxjs'; import { Observable, of, forkJoin, Subject } from 'rxjs';
import { switchMap } from 'rxjs/operators'; import { switchMap, takeUntil } from 'rxjs/operators';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { FormBaseComponent, FormFieldModel, FormOutcomeEvent, FormOutcomeModel, WidgetVisibilityService, FormService, NotificationService } from '@alfresco/adf-core'; import { FormBaseComponent,
FormFieldModel,
FormOutcomeEvent,
FormOutcomeModel,
WidgetVisibilityService,
FormService,
NotificationService,
FormRenderingService } from '@alfresco/adf-core';
import { FormCloudService } from '../services/form-cloud.service'; import { FormCloudService } from '../services/form-cloud.service';
import { FormCloud } from '../models/form-cloud.model'; import { FormCloud } from '../models/form-cloud.model';
import { TaskVariableCloud } from '../models/task-variable-cloud.model'; import { TaskVariableCloud } from '../models/task-variable-cloud.model';
import { DropdownCloudWidgetComponent } from './dropdown-cloud/dropdown-cloud.widget';
import { UploadCloudWidgetComponent } from './upload-cloud.widget';
@Component({ @Component({
selector: 'adf-cloud-form', selector: 'adf-cloud-form',
templateUrl: './form-cloud.component.html' templateUrl: './form-cloud.component.html'
}) })
export class FormCloudComponent extends FormBaseComponent implements OnChanges { export class FormCloudComponent extends FormBaseComponent implements OnChanges, OnDestroy {
/** App id to fetch corresponding form and values. */ /** App id to fetch corresponding form and values. */
@Input() @Input()
@@ -75,15 +84,22 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges {
protected subscriptions: Subscription[] = []; protected subscriptions: Subscription[] = [];
nodeId: string; nodeId: string;
protected onDestroy$ = new Subject<boolean>();
constructor(protected formCloudService: FormCloudService, constructor(protected formCloudService: FormCloudService,
protected formService: FormService, protected formService: FormService,
private notificationService: NotificationService, private notificationService: NotificationService,
private formRenderingService: FormRenderingService,
protected visibilityService: WidgetVisibilityService) { protected visibilityService: WidgetVisibilityService) {
super(); super();
this.formService.formContentClicked.subscribe((content: any) => { this.formService.formContentClicked
.pipe(takeUntil(this.onDestroy$))
.subscribe((content: any) => {
this.formContentClicked.emit(content); this.formContentClicked.emit(content);
}); });
this.formRenderingService.setComponentTypeResolver('upload', () => UploadCloudWidgetComponent, true);
this.formRenderingService.setComponentTypeResolver('dropdown', () => DropdownCloudWidgetComponent, true);
} }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
@@ -152,6 +168,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges {
return new Promise<FormCloud>((resolve, reject) => { return new Promise<FormCloud>((resolve, reject) => {
forkJoin(this.formCloudService.getTaskForm(appName, taskId), forkJoin(this.formCloudService.getTaskForm(appName, taskId),
this.formCloudService.getTaskVariables(appName, taskId)) this.formCloudService.getTaskVariables(appName, taskId))
.pipe(takeUntil(this.onDestroy$))
.subscribe( .subscribe(
(data) => { (data) => {
this.data = data[1]; this.data = data[1];
@@ -174,6 +191,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges {
getFormById(appName: string, formId: string) { getFormById(appName: string, formId: string) {
this.formCloudService this.formCloudService
.getForm(appName, formId) .getForm(appName, formId)
.pipe(takeUntil(this.onDestroy$))
.subscribe( .subscribe(
(form) => { (form) => {
const parsedForm = this.parseForm(form); const parsedForm = this.parseForm(form);
@@ -220,6 +238,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges {
if (this.form && this.appName && this.taskId) { if (this.form && this.appName && this.taskId) {
this.formCloudService this.formCloudService
.saveTaskForm(this.appName, this.taskId, this.form.id, this.form.values) .saveTaskForm(this.appName, this.taskId, this.form.id, this.form.values)
.pipe(takeUntil(this.onDestroy$))
.subscribe( .subscribe(
() => { () => {
this.onTaskSaved(this.form); this.onTaskSaved(this.form);
@@ -233,6 +252,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges {
if (this.form && this.appName && this.taskId) { if (this.form && this.appName && this.taskId) {
this.formCloudService this.formCloudService
.completeTaskForm(this.appName, this.taskId, this.form.id, this.form.values, outcome) .completeTaskForm(this.appName, this.taskId, this.form.id, this.form.values, outcome)
.pipe(takeUntil(this.onDestroy$))
.subscribe( .subscribe(
() => { () => {
this.onTaskCompleted(this.form); this.onTaskCompleted(this.form);
@@ -319,4 +339,9 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges {
protected storeFormAsMetadata() { protected storeFormAsMetadata() {
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
} }

View File

@@ -18,7 +18,7 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout'; import { FlexLayoutModule } from '@angular/flex-layout';
import { TemplateModule, FormBaseModule, PipeModule, CoreModule } from '@alfresco/adf-core'; import { TemplateModule, FormBaseModule, PipeModule, CoreModule, FormRenderingService } from '@alfresco/adf-core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { UploadCloudWidgetComponent } from './components/upload-cloud.widget'; import { UploadCloudWidgetComponent } from './components/upload-cloud.widget';
import { MaterialModule } from '../material.module'; import { MaterialModule } from '../material.module';
@@ -42,7 +42,8 @@ import { DropdownCloudWidgetComponent } from './components/dropdown-cloud/dropdo
], ],
declarations: [FormCloudComponent, UploadCloudWidgetComponent, FormDefinitionSelectorCloudComponent, FormCustomOutcomesComponent, DropdownCloudWidgetComponent], declarations: [FormCloudComponent, UploadCloudWidgetComponent, FormDefinitionSelectorCloudComponent, FormCustomOutcomesComponent, DropdownCloudWidgetComponent],
providers: [ providers: [
FormDefinitionSelectorCloudService FormDefinitionSelectorCloudService,
FormRenderingService
], ],
entryComponents: [ entryComponents: [
UploadCloudWidgetComponent, UploadCloudWidgetComponent,