Skip to content

Commit 31e46cb

Browse files
committed
🎨 Add @ActionsGroup and @action support #56
fix #56
1 parent 5cb2e30 commit 31e46cb

File tree

6 files changed

+151
-67
lines changed

6 files changed

+151
-67
lines changed

demo/src/main/java/io/asfjava/ui/demo/screen/DemoForm.java

+12-44
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,23 @@
22

33
import java.io.Serializable;
44

5-
import io.asfjava.ui.core.form.Index;
6-
import io.asfjava.ui.core.form.Tab;
5+
import io.asfjava.ui.core.form.Action;
6+
import io.asfjava.ui.core.form.ActionsGroup;
77
import io.asfjava.ui.core.form.TextField;
88

9+
@ActionsGroup({ @Action(title = "Next", type = "button", onClick = "goToNext()"),
10+
@Action(title = "Previous", type = "button", onClick = "goToPrevious()") })
11+
@Action(title = "Previous", type = "button", onClick = "goToPrevious()")
12+
@Action(title = "Next", type = "button", onClick = "goToNext()")
13+
@Action(title = "Send", type = "submit")
914
public class DemoForm implements Serializable {
1015

11-
@Index(1)
12-
@TextField(title = "index 1")
13-
private String index1;
16+
@TextField(title = "mail", pattern="^\\S+@\\S+$")
17+
private String mail;
1418

15-
@Tab(index = 2, title = "Tab2")
16-
@Index(0)
17-
@TextField(title = "index 2")
18-
private String index2;
19-
20-
@Tab(index = 3, title = "Tab1")
21-
@Index(2)
22-
@TextField(title = "Tab1 f3 index 2")
23-
private String index3;
24-
25-
@Tab(index = 3, title = "Tab1")
26-
@Index(1)
27-
@TextField(title = "Tab1 f4 index 1")
28-
private String index4;
29-
30-
@Tab(index = 2, title = "Tab2")
31-
@TextField(title = "Tab2 f5 index 0")
32-
private String index5;
33-
34-
private static final long serialVersionUID = -5073515619469444978L;
35-
36-
public String getIndex1() {
37-
return index1;
38-
}
39-
40-
public String getIndex2() {
41-
return index2;
42-
}
43-
44-
public String getIndex4() {
45-
return index4;
46-
}
47-
48-
public String getIndex5() {
49-
return index5;
50-
}
51-
52-
public String getIndex3() {
53-
return index3;
19+
public String getMail() {
20+
return mail;
5421
}
5522

23+
private static final long serialVersionUID = -5073515619469444978L;
5624
}

demo/src/main/resources/static/index.html

+8-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<script src="../bower_components/angular-schema-form-bootstrap/bootstrap-decorator.min.js"></script>
99
<script src="../bower_components/lodash/lodash.js"></script>
1010

11-
<link rel="stylesheet" href="../bower_components/bootstrap/dist/css/bootstrap.min.css">
11+
<link rel="stylesheet" href="../bower_components/bootstrap/dist/css/bootstrap.min.css">
1212

1313
<script>
1414

@@ -24,6 +24,13 @@
2424
function errorCallback(error){
2525
//error code
2626
}
27+
28+
$scope.goToNext = function($event,item){
29+
alert("Click on next button")
30+
}
31+
$scope.goToPrevious = function(){
32+
alert("Click on previous button")
33+
}
2734
});
2835

2936
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package io.asfjava.ui.core.form;
2+
3+
import java.lang.annotation.Repeatable;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
7+
@Retention(RetentionPolicy.RUNTIME)
8+
@Repeatable(Actions.class)
9+
public @interface Action {
10+
String type();
11+
12+
String title();
13+
14+
String onClick() default "";
15+
}
16+
17+
@Retention(RetentionPolicy.RUNTIME)
18+
@interface Actions {
19+
Action[] value();
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.asfjava.ui.core.form;
2+
3+
import static java.lang.annotation.ElementType.TYPE;
4+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
5+
6+
import java.lang.annotation.Retention;
7+
import java.lang.annotation.Target;
8+
9+
@Retention(RUNTIME)
10+
@Target(TYPE)
11+
public @interface ActionsGroup {
12+
Action[] value();
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.asfjava.ui.core.form;
2+
3+
import static java.lang.annotation.ElementType.FIELD;
4+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
5+
6+
import java.lang.annotation.Retention;
7+
import java.lang.annotation.Target;
8+
9+
@Retention(RUNTIME)
10+
@Target(FIELD)
11+
public @interface FieldSet {
12+
String title() default "";
13+
}

src/main/java/io/asfjava/ui/core/schema/UiFormSchemaGenerator.java

+85-22
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.util.List;
1212
import java.util.Map;
1313
import java.util.Map.Entry;
14+
import java.util.Optional;
1415
import java.util.function.Predicate;
1516
import java.util.stream.Collectors;
1617

@@ -23,6 +24,9 @@
2324
import com.fasterxml.jackson.module.jsonSchema.JsonSchemaGenerator;
2425

2526
import io.asfjava.ui.core.FormDefinitionGeneratorFactory;
27+
import io.asfjava.ui.core.form.Action;
28+
import io.asfjava.ui.core.form.ActionsGroup;
29+
import io.asfjava.ui.core.form.FieldSet;
2630
import io.asfjava.ui.core.form.Index;
2731
import io.asfjava.ui.core.form.Tab;
2832
import io.asfjava.ui.dto.UiForm;
@@ -38,39 +42,81 @@ public UiForm generate(Class<? extends Serializable> formDto) throws JsonMapping
3842
JsonSchemaGenerator schemaGen = initSchemaGen(mapper);
3943
JsonSchema schema = generateSchema(formDto, schemaGen);
4044

41-
Map<Field, JsonNode> nodes = initFieldFormDefinition(mapper, declaredFields);
45+
Map<Field, JsonNode> nodes = initFieldsFormDefinition(mapper, declaredFields);
4246

4347
Map<Field, JsonNode> sortedNodes = reorderFieldsBasedOnIndex(nodes);
4448

45-
handlerGroupedFields();
49+
handlerGroupedFields(mapper, declaredFields, sortedNodes);
4650

47-
ObjectNode tabbedFields = handleTabbedFields(mapper, declaredFields, sortedNodes);
51+
Optional<ObjectNode> tabbedFields = Optional
52+
.ofNullable(handleTabbedFields(mapper, declaredFields, sortedNodes));
4853

4954
ArrayNode formDefinition = mapper.createArrayNode();
50-
formDefinition.add(tabbedFields);
55+
tabbedFields.ifPresent(formDefinition::add);
5156
sortedNodes.entrySet().stream().forEach(nodesElement -> formDefinition.add(nodesElement.getValue()));
5257

58+
handleActionsAnnotation(mapper, formDto, formDefinition);
59+
5360
return new UiForm(schema, formDefinition);
5461
}
5562

56-
private Map<Field, JsonNode> reorderFieldsBasedOnIndex(Map<Field, JsonNode> nodes) {
63+
private void handleActionsAnnotation(ObjectMapper mapper, Class<? extends Serializable> formDto,
64+
ArrayNode formDefinition) {
65+
ObjectNode groupedActionsNode = mapper.createObjectNode();
66+
ObjectNode actionsNode = mapper.createObjectNode();
5767

58-
Comparator<? super Entry<Field, JsonNode>> tabIndexComparator = (entry1, entry2) -> {
68+
buildActions(mapper, formDto, actionsNode, formDefinition);
69+
buildGroupedActions(mapper, formDto, groupedActionsNode, formDefinition);
70+
}
5971

60-
Index field1Index = entry1.getKey().getAnnotation(Index.class);
61-
Index field2Index = entry2.getKey().getAnnotation(Index.class);
72+
private void buildActions(ObjectMapper mapper, Class<? extends Serializable> formDto, ObjectNode actionsNode,
73+
ArrayNode formDefinition) {
6274

63-
return Integer.compare((field1Index != null ? field1Index.value() : Integer.MAX_VALUE),
64-
field2Index != null ? field2Index.value() : Integer.MAX_VALUE);
65-
};
75+
Action[] actionsAnnotations = formDto.getAnnotationsByType(Action.class);
76+
Arrays.stream(actionsAnnotations).forEach(action -> {
77+
formDefinition.add(buildActionNode(mapper, action));
78+
});
79+
}
6680

67-
return nodes.entrySet().stream().sorted(tabIndexComparator).collect(Collectors.toMap(Map.Entry::getKey,
68-
Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new));
81+
private void buildGroupedActions(ObjectMapper mapper, Class<? extends Serializable> formDto, ObjectNode actionsNode,
82+
ArrayNode formDefinition) {
83+
Optional<ActionsGroup> actionsAnnotation = Optional.ofNullable(formDto.getAnnotation(ActionsGroup.class));
84+
actionsAnnotation.ifPresent(actions -> {
85+
actionsNode.put("type", "actions");
86+
ArrayNode items = mapper.createArrayNode();
87+
Arrays.stream(actions.value()).forEach(action -> {
88+
ObjectNode node = buildActionNode(mapper, action);
89+
items.add(node);
90+
});
91+
actionsNode.set("items", items);
92+
93+
formDefinition.add(actionsNode);
94+
});
95+
}
6996

97+
private ObjectNode buildActionNode(ObjectMapper mapper, Action action) {
98+
ObjectNode node = mapper.createObjectNode();
99+
node.put("type", action.type());
100+
node.put("title", action.title());
101+
node.put("onClick", action.onClick());
102+
return node;
70103
}
71104

72-
private void handlerGroupedFields() {
73-
// TODO Grouping fieldset must handle it
105+
private ObjectNode handlerGroupedFields(ObjectMapper mapper, Field[] declaredFields,
106+
Map<Field, JsonNode> sortedNodes) {
107+
Predicate<? super Field> checkFieldSetAnnotation = field -> field.isAnnotationPresent(FieldSet.class);
108+
109+
Map<String, List<JsonNode>> groupedFields = new LinkedHashMap<>();
110+
111+
Arrays.stream(declaredFields).filter(checkFieldSetAnnotation)
112+
.forEach(field -> groupFieldsByTab(sortedNodes, field, groupedFields));
113+
114+
ArrayNode groups = mapper.createArrayNode();
115+
116+
ObjectNode tabsNode = mapper.createObjectNode();
117+
tabsNode.put("type", "fieldset");
118+
tabsNode.set("items", groups);
119+
return tabsNode;
74120

75121
}
76122

@@ -83,7 +129,6 @@ private ObjectNode handleTabbedFields(ObjectMapper mapper, Field[] declaredField
83129
Comparator<? super Field> fieldIndexComparator = (entry1, entry2) -> {
84130
Index field1Index = entry1.getAnnotation(Index.class);
85131
Index field2Index = entry2.getAnnotation(Index.class);
86-
87132
return Integer.compare((field1Index != null ? field1Index.value() : Integer.MAX_VALUE),
88133
field2Index != null ? field2Index.value() : Integer.MAX_VALUE);
89134
};
@@ -103,15 +148,17 @@ private ObjectNode handleTabbedFields(ObjectMapper mapper, Field[] declaredField
103148
tabNode.set("items", tabItems);
104149
tabs.add(tabNode);
105150
});
106-
107-
ObjectNode tabsNode = mapper.createObjectNode();
108-
tabsNode.put("type", "tabs");
109-
tabsNode.set("tabs", tabs);
110-
return tabsNode;
151+
if (tabs.size() > 0) {
152+
ObjectNode tabsNode = mapper.createObjectNode();
153+
tabsNode.put("type", "tabs");
154+
tabsNode.set("tabs", tabs);
155+
return tabsNode;
156+
}
157+
return null;
111158

112159
}
113160

114-
private Map<Field, JsonNode> initFieldFormDefinition(ObjectMapper mapper, Field[] declaredFields) {
161+
private Map<Field, JsonNode> initFieldsFormDefinition(ObjectMapper mapper, Field[] declaredFields) {
115162
Map<Field, JsonNode> nodes = new HashMap<>();
116163

117164
Arrays.stream(declaredFields).forEach(field -> buildFormDefinition(nodes, mapper, field));
@@ -154,6 +201,22 @@ private void buildFieldDefinition(Field field, Annotation annotation, ObjectMapp
154201
});
155202
}
156203

204+
private Map<Field, JsonNode> reorderFieldsBasedOnIndex(Map<Field, JsonNode> nodes) {
205+
206+
Comparator<? super Entry<Field, JsonNode>> tabIndexComparator = (entry1, entry2) -> {
207+
208+
Index field1Index = entry1.getKey().getAnnotation(Index.class);
209+
Index field2Index = entry2.getKey().getAnnotation(Index.class);
210+
211+
return Integer.compare((field1Index != null ? field1Index.value() : Integer.MAX_VALUE),
212+
field2Index != null ? field2Index.value() : Integer.MAX_VALUE);
213+
};
214+
215+
return nodes.entrySet().stream().sorted(tabIndexComparator).collect(Collectors.toMap(Map.Entry::getKey,
216+
Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new));
217+
218+
}
219+
157220
public static UiFormSchemaGenerator get() {
158221
if (instance == null) {
159222
instance = new UiFormSchemaGenerator();

0 commit comments

Comments
 (0)