Skip to content

Commit 4bdab45

Browse files
mateusjmafjhosefmarks
authored andcommitted
feat(dynamic-form): permite atribuir foco nos campos
Agora é possível atribuir foco ao campo desejado. Isto poderá ser feito na inicialização do componente através da propriedade `p-auto-focus` atribuindo o nome do campo à ela ou, disparando o método `focus` e passando como parâmetro o nome do campo. Fixes DTHFUI-2415
1 parent 6a861e9 commit 4bdab45

12 files changed

+182
-30
lines changed

projects/ui/src/lib/components/po-dynamic/po-dynamic-form/po-dynamic-form-base.component.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,22 @@ export class PoDynamicFormBaseComponent {
2020

2121
private _groupForm?: boolean = false;
2222

23+
/**
24+
* @optional
25+
*
26+
* @description
27+
*
28+
* Nome da propriedade, atribuída ao `PoDynamicFormField.property`, que iniciará o campo com foco.
29+
*
30+
* > Não é possivel iniciar os componentes abaixo com foco:
31+
* - `po-checkbox-group`
32+
* - `po-combo`
33+
* - `po-radio-group`
34+
* - `po-select`
35+
* - `po-switch`
36+
*/
37+
@Input('p-auto-focus') autoFocus?: string;
38+
2339
/**
2440
* @description
2541
*

projects/ui/src/lib/components/po-dynamic/po-dynamic-form/po-dynamic-form-field.interface.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export interface PoDynamicFormField extends PoDynamicField {
8080
/** Valor mínimo a ser informado no componente, podendo ser utilizado quando o tipo de dado por *number*, *date* ou *dateTime*. */
8181
minValue?: string | number;
8282

83-
/** Quantidade de linhas exibidas no po-textarea */
83+
/** Quantidade de linhas exibidas no `po-textarea`. */
8484
rows?: number;
8585

8686
/** Esconde a informação estilo *password*, pode ser utilizado quando o tipo de dado for *string*. */

projects/ui/src/lib/components/po-dynamic/po-dynamic-form/po-dynamic-form-fields/po-dynamic-form-field-internal.interface.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,16 @@ export interface PoDynamicFormFieldInternal extends PoDynamicFormField {
1515
// camponente a ser utilizado.
1616
control?: string;
1717

18+
/**
19+
* Aplica foco no campo quando o componente for iniciado.
20+
*
21+
* > Os seguintes componentes ainda não contemplam esta propriedade:
22+
* - `po-checkbox-group`
23+
* - `po-combo`
24+
* - `po-radio-group`
25+
* - `po-select`
26+
* - `po-switch`
27+
*/
28+
focus?: boolean;
29+
1830
}

projects/ui/src/lib/components/po-dynamic/po-dynamic-form/po-dynamic-form-fields/po-dynamic-form-fields-base.component.spec.ts

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ describe('PoDynamicFormFieldsBaseComponent:', () => {
2828
expect(component).toBeTruthy();
2929
});
3030

31-
describe('Properties: ', () => {
31+
describe('Properties:', () => {
3232
it('fields: should set `fields` to `[]` if not Array value' , () => {
3333
const invalidValues = [undefined, null, '', true, false, 0, 1, 'string', {}];
3434

@@ -133,13 +133,14 @@ describe('PoDynamicFormFieldsBaseComponent:', () => {
133133
expect(convertedOptions.length).toBe(options.length);
134134
});
135135

136-
it(`createField: should call 'getGridColumnsClasses', and not call 'convertOptions' and return an
137-
object that overrides the values of the same properties`, () => {
136+
it(`createField: should call 'getGridColumnsClasses' and 'hasFocus', not call 'convertOptions' and return an
137+
object that overrides the values of the same properties.`, () => {
138138
const field = { property: 'propertyName', label: 'labelName' };
139139

140140
const spyTitleCasePipeTransform = spyOn(component['titleCasePipe'], 'transform').and.returnValue('propertyName');
141141
const spyGetGridColumnsClasses = spyOn(PoDynamicUtil, 'getGridColumnsClasses').and.callThrough();
142142
const spyConvertOptions = spyOn(component, <any> 'convertOptions').and.callThrough();
143+
const spyHasFocus = spyOn(component, <any> 'hasFocus');
143144

144145
const newField = component['createField'](field);
145146

@@ -150,15 +151,17 @@ describe('PoDynamicFormFieldsBaseComponent:', () => {
150151
expect(spyTitleCasePipeTransform).toHaveBeenCalled();
151152
expect(spyGetGridColumnsClasses).toHaveBeenCalled();
152153
expect(spyConvertOptions).not.toHaveBeenCalled();
154+
expect(spyHasFocus).toHaveBeenCalled();
153155
});
154156

155-
it(`createField: should call 'getGridColumnsClasses', 'convertOptions' and return an
157+
it(`createField: should call 'getGridColumnsClasses', 'convertOptions', 'hasFocus' and return an
156158
object that overrides the values of the same properties`, () => {
157159
const field = { property: 'propertyName', label: 'labelName', options: ['Option 1', 'Option 2'] };
158160

159161
const spyTitleCasePipeTransform = spyOn(component['titleCasePipe'], 'transform').and.returnValue('propertyName');
160162
const spyGetGridColumnsClasses = spyOn(PoDynamicUtil, 'getGridColumnsClasses').and.callThrough();
161163
const spyConvertOptions = spyOn(component, <any> 'convertOptions').and.callThrough();
164+
const spyHasFocus = spyOn(component, <any> 'hasFocus');
162165

163166
const newField = component['createField'](field);
164167

@@ -169,6 +172,31 @@ describe('PoDynamicFormFieldsBaseComponent:', () => {
169172
expect(spyTitleCasePipeTransform).toHaveBeenCalled();
170173
expect(spyGetGridColumnsClasses).toHaveBeenCalled();
171174
expect(spyConvertOptions).toHaveBeenCalled();
175+
expect(spyHasFocus).toHaveBeenCalled();
176+
});
177+
178+
it(`hasFocus: should return true if 'autoFocus' is equal to 'field.property'`, () => {
179+
const field = { property: 'field' };
180+
181+
component.autoFocus = 'field';
182+
183+
expect(component['hasFocus'](field)).toBe(true);
184+
});
185+
186+
it(`hasFocus: should return undefined if 'autoFocus' is undefined`, () => {
187+
const field = { property: 'field' };
188+
189+
component.autoFocus = undefined;
190+
191+
expect(component['hasFocus'](field)).toBe(false);
192+
});
193+
194+
it(`hasFocus: should return undefined if 'autoFocus' isn't equal to 'field.property'`, () => {
195+
const field = { property: 'field' };
196+
197+
component.autoFocus = 'otherField';
198+
199+
expect(component['hasFocus'](field)).toBe(false);
172200
});
173201

174202
it('existsProperty: should return `true` if property exists in fields', () => {

projects/ui/src/lib/components/po-dynamic/po-dynamic-form/po-dynamic-form-fields/po-dynamic-form-fields-base.component.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export class PoDynamicFormFieldsBaseComponent {
1515

1616
visibleFields: Array<PoDynamicFormFieldInternal> = [];
1717

18+
@Input('p-auto-focus') autoFocus?: string;
19+
1820
// array de objetos que implementam a interface PoDynamicFormField, que serão exibidos no componente.
1921
@Input('p-fields') set fields(value: Array<PoDynamicFormField>) {
2022
this._fields = Array.isArray(value) ? [...value] : [];
@@ -77,6 +79,7 @@ export class PoDynamicFormFieldsBaseComponent {
7779
private createField(field: PoDynamicFormField): PoDynamicFormFieldInternal {
7880
const control = this.getComponentControl(field);
7981
const options = !!field.options ? this.convertOptions(field.options) : undefined;
82+
const focus = this.hasFocus(field);
8083

8184
const componentClass = getGridColumnsClasses(field.gridSmColumns,
8285
field.gridMdColumns,
@@ -89,6 +92,7 @@ export class PoDynamicFormFieldsBaseComponent {
8992
...field,
9093
componentClass,
9194
control,
95+
focus,
9296
options
9397
};
9498
}
@@ -136,6 +140,10 @@ export class PoDynamicFormFieldsBaseComponent {
136140
return 'input';
137141
}
138142

143+
private hasFocus(field: PoDynamicFormField) {
144+
return !!this.autoFocus && this.autoFocus === field.property;
145+
}
146+
139147
private isCheckboxGroup(field: PoDynamicFormField) {
140148
const { optionsService, optionsMulti, options } = field;
141149

projects/ui/src/lib/components/po-dynamic/po-dynamic-form/po-dynamic-form-fields/po-dynamic-form-fields.component.html

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,43 @@
44
<po-divider *ngIf="field?.divider?.trim()" class="po-sm-12" [p-label]="field.divider">
55
</po-divider>
66

7-
<po-datepicker *ngIf="compareTo(field.control, 'datepicker')"
7+
<po-datepicker #component *ngIf="compareTo(field.control, 'datepicker')"
88
[name]="field.property"
99
[(ngModel)]="value[field.property]"
1010
[ngClass]="field.componentClass"
1111
p-clean
1212
[p-disabled]="field.disabled"
13+
[p-focus]="field.focus"
1314
[p-help]="field.help"
1415
[p-label]="field.label"
1516
[p-max-date]="field.maxValue"
1617
[p-min-date]="field.minValue"
1718
[p-required]="field.required">
1819
</po-datepicker>
1920

20-
<po-input *ngIf="compareTo(field.control, 'input')"
21+
<po-input #component *ngIf="compareTo(field.control, 'input')"
2122
[name]="field.property"
2223
[(ngModel)]="value[field.property]"
2324
[ngClass]="field.componentClass"
2425
p-clean
2526
[p-disabled]="field.disabled"
27+
[p-focus]="field.focus"
2628
[p-help]="field.help"
2729
[p-label]="field.label"
2830
[p-mask]="field.mask"
2931
[p-maxlength]="field.maxLength"
3032
[p-minlength]="field.minLength"
3133
[p-pattern]="field.pattern"
3234
[p-required]="field.required">
33-
</po-input>
35+
</po-input>
3436

35-
<po-number *ngIf="compareTo(field.control, 'number')"
37+
<po-number #component *ngIf="compareTo(field.control, 'number')"
3638
[name]="field.property"
3739
[(ngModel)]="value[field.property]"
3840
[ngClass]="field.componentClass"
3941
p-clean
4042
[p-disabled]="field.disabled"
43+
[p-focus]="field.focus"
4144
[p-help]="field.help"
4245
[p-label]="field.label"
4346
[p-min]="field.minValue"
@@ -47,18 +50,19 @@
4750
[p-required]="field.required">
4851
</po-number>
4952

50-
<po-decimal *ngIf="compareTo(field.control, 'decimal')"
53+
<po-decimal #component *ngIf="compareTo(field.control, 'decimal')"
5154
[name]="field.property"
5255
[(ngModel)]="value[field.property]"
5356
[ngClass]="field.componentClass"
5457
p-clean
5558
[p-disabled]="field.disabled"
59+
[p-focus]="field.focus"
5660
[p-help]="field.help"
5761
[p-label]="field.label"
5862
[p-required]="field.required">
5963
</po-decimal>
6064

61-
<po-select *ngIf="compareTo(field.control, 'select')"
65+
<po-select #component *ngIf="compareTo(field.control, 'select')"
6266
[name]="field.property"
6367
[(ngModel)]="value[field.property]"
6468
[ngClass]="field.componentClass"
@@ -69,7 +73,7 @@
6973
[p-required]="field.required">
7074
</po-select>
7175

72-
<po-radio-group *ngIf="compareTo(field.control, 'radioGroup')"
76+
<po-radio-group #component *ngIf="compareTo(field.control, 'radioGroup')"
7377
[name]="field.property"
7478
[(ngModel)]="value[field.property]"
7579
[ngClass]="field.componentClass"
@@ -81,7 +85,7 @@
8185
[p-required]="field.required">
8286
</po-radio-group>
8387

84-
<po-switch *ngIf="compareTo(field.control, 'switch')"
88+
<po-switch #component *ngIf="compareTo(field.control, 'switch')"
8589
[name]="field.property"
8690
[(ngModel)]="value[field.property]"
8791
[ngClass]="field.componentClass"
@@ -92,7 +96,7 @@
9296
[p-label-on]="field.booleanTrue">
9397
</po-switch>
9498

95-
<po-combo *ngIf="compareTo(field.control, 'combo')"
99+
<po-combo #component *ngIf="compareTo(field.control, 'combo')"
96100
[name]="field.property"
97101
[(ngModel)]="value[field.property]"
98102
[ngClass]="field.componentClass"
@@ -103,7 +107,7 @@
103107
[p-required]="field.required">
104108
</po-combo>
105109

106-
<po-lookup *ngIf="compareTo(field.control, 'lookup')"
110+
<po-lookup #component *ngIf="compareTo(field.control, 'lookup')"
107111
[name]="field.property"
108112
[(ngModel)]="value[field.property]"
109113
p-field-label="label"
@@ -112,12 +116,13 @@
112116
[p-columns]="field.columns"
113117
[p-disabled]="field.disabled"
114118
[p-filter-service]="field.searchService"
119+
[p-focus]="field.focus"
115120
[p-help]="field.help"
116121
[p-label]="field.label"
117122
[p-required]="field.required">
118123
</po-lookup>
119124

120-
<po-checkbox-group *ngIf="compareTo(field.control, 'checkboxGroup')"
125+
<po-checkbox-group #component *ngIf="compareTo(field.control, 'checkboxGroup')"
121126
[name]="field.property"
122127
[(ngModel)]="value[field.property]"
123128
[ngClass]="field.componentClass"
@@ -129,22 +134,24 @@
129134
[p-required]="field.required">
130135
</po-checkbox-group>
131136

132-
<po-multiselect *ngIf="compareTo(field.control, 'multiselect')"
137+
<po-multiselect #component *ngIf="compareTo(field.control, 'multiselect')"
133138
[name]="field.property"
134139
[(ngModel)]="value[field.property]"
135140
[ngClass]="field.componentClass"
136141
[p-disabled]="field.disabled"
142+
[p-focus]="field.focus"
137143
[p-help]="field.help"
138144
[p-label]="field.label"
139145
[p-options]="field.options"
140146
[p-required]="field.required">
141147
</po-multiselect>
142148

143-
<po-textarea *ngIf="compareTo(field.control, 'textarea')"
149+
<po-textarea #component *ngIf="compareTo(field.control, 'textarea')"
144150
[name]="field.property"
145151
[(ngModel)]="value[field.property]"
146152
[ngClass]="field.componentClass"
147153
[p-disabled]="field.disabled"
154+
[p-focus]="field.focus"
148155
[p-help]="field.help"
149156
[p-label]="field.label"
150157
[p-maxlength]="field.maxLength"
@@ -153,18 +160,19 @@
153160
[p-rows]="field.rows">
154161
</po-textarea>
155162

156-
<po-password *ngIf="compareTo(field.control, 'password')"
163+
<po-password #component *ngIf="compareTo(field.control, 'password')"
157164
[name]="field.property"
158165
[(ngModel)]="value[field.property]"
159166
[ngClass]="field.componentClass"
160167
p-clean
161168
[p-disabled]="field.disabled"
169+
[p-focus]="field.focus"
162170
[p-help]="field.help"
163171
[p-label]="field.label"
164172
[p-maxlength]="field.maxLength"
165173
[p-minlength]="field.minLength"
166174
[p-required]="field.required">
167-
</po-password>
175+
</po-password>
168176

169177
</ng-container>
170178
</div>

projects/ui/src/lib/components/po-dynamic/po-dynamic-form/po-dynamic-form-fields/po-dynamic-form-fields.component.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,26 @@ describe('PoDynamicFormFieldsComponent: ', () => {
6767
expect(component.trackBy(index)).toBe(index);
6868
});
6969

70+
it('focus: should call `fieldComponent.focus` if find field by property param.', () => {
71+
const fieldComponent = { focus: () => {}, name: 'newField' };
72+
component.components = <any>[fieldComponent];
73+
74+
const spyOnFocus = spyOn(fieldComponent, 'focus');
75+
component.focus('newField');
76+
77+
expect(spyOnFocus).toHaveBeenCalled();
78+
});
79+
80+
it('focus: shouldn´t call `fieldsComponent.focus` if not find field by property param.', () => {
81+
const fieldComponent = { focus: () => {}, name: 'newField' };
82+
component.components = <any>[fieldComponent];
83+
84+
const spyOnFocus = spyOn(fieldComponent, 'focus');
85+
component.focus('otherField');
86+
87+
expect(spyOnFocus).not.toHaveBeenCalled();
88+
});
89+
7090
});
7191

7292
describe('Templates: ', () => {

projects/ui/src/lib/components/po-dynamic/po-dynamic-form/po-dynamic-form-fields/po-dynamic-form-fields.component.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { Component, SimpleChanges, OnChanges } from '@angular/core';
1+
import { Component, OnChanges, QueryList, SimpleChanges, ViewChildren } from '@angular/core';
2+
import { ControlContainer, NgForm } from '@angular/forms';
3+
import { TitleCasePipe } from '@angular/common';
24

35
import { PoDynamicFormFieldsBaseComponent } from './po-dynamic-form-fields-base.component';
4-
import { TitleCasePipe } from '@angular/common';
5-
import { ControlContainer, NgForm } from '@angular/forms';
66

77
/**
88
* @docsPrivate
@@ -18,6 +18,8 @@ import { ControlContainer, NgForm } from '@angular/forms';
1818
})
1919
export class PoDynamicFormFieldsComponent extends PoDynamicFormFieldsBaseComponent implements OnChanges {
2020

21+
@ViewChildren('component') components: QueryList<{ name: string, focus: () => void }>;
22+
2123
constructor(titleCasePipe: TitleCasePipe) {
2224
super(titleCasePipe);
2325
}
@@ -28,6 +30,13 @@ export class PoDynamicFormFieldsComponent extends PoDynamicFormFieldsBaseCompone
2830
}
2931
}
3032

33+
focus(property: string) {
34+
const foundComponent = this.components.find(component => component.name === property);
35+
if (foundComponent) {
36+
foundComponent.focus();
37+
}
38+
}
39+
3140
trackBy(index) {
3241
return index;
3342
}

0 commit comments

Comments
 (0)