visibility rule fixes
This commit is contained in:
parent
48eb38a156
commit
7790d19818
|
@ -13,7 +13,7 @@ public class PropertyDefinition {
|
|||
private final Map<String, PropertyDefinitionFieldSet> fieldSets;
|
||||
|
||||
public Map<String, PropertyDefinitionFieldSet> getFieldSets() {
|
||||
return fieldSets;
|
||||
return this.fieldSets;
|
||||
}
|
||||
|
||||
public PropertyDefinition(PropertyDefinitionPersist persist){
|
||||
|
|
|
@ -13,7 +13,7 @@ public class PropertyDefinitionFieldSet {
|
|||
private final List<PropertyDefinitionFieldSetItem> items;
|
||||
|
||||
public List<PropertyDefinitionFieldSetItem> getItems() {
|
||||
return items;
|
||||
return this.items;
|
||||
}
|
||||
|
||||
public PropertyDefinitionFieldSet(PropertyDefinitionFieldSetPersist persist){
|
||||
|
|
|
@ -15,12 +15,12 @@ public class PropertyDefinitionFieldSetItem {
|
|||
private final Integer ordinal;
|
||||
|
||||
public Map<String, Field> getFields() {
|
||||
return fields;
|
||||
return this.fields;
|
||||
}
|
||||
|
||||
|
||||
public Integer getOrdinal() {
|
||||
return ordinal;
|
||||
return this.ordinal;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ public class VisibilityServiceImpl implements VisibilityService {
|
|||
private final PropertyDefinition propertyDefinition;
|
||||
|
||||
private Map<String, List<RuleWithTarget>> rulesBySources;
|
||||
private Map<String, List<RuleWithTarget>> rulesByTarget;
|
||||
private Map<FieldKey, Boolean> visibility;
|
||||
|
||||
public VisibilityServiceImpl(DefinitionEntity definition, PropertyDefinitionPersist propertyDefinition) {
|
||||
|
@ -27,14 +28,18 @@ public class VisibilityServiceImpl implements VisibilityService {
|
|||
}
|
||||
|
||||
private void initRules(){
|
||||
if (this.rulesBySources != null) return;
|
||||
if (this.rulesBySources != null && this.rulesByTarget != null) return;
|
||||
this.rulesBySources = new HashMap<>();
|
||||
this.rulesByTarget = new HashMap<>();
|
||||
for (FieldEntity fieldEntity : this.definition.getAllField()){
|
||||
if (fieldEntity.getVisibilityRules() != null && !fieldEntity.getVisibilityRules().isEmpty()) {
|
||||
for (RuleEntity rule : fieldEntity.getVisibilityRules()){
|
||||
if (!this.rulesBySources.containsKey(fieldEntity.getId())) this.rulesBySources.put(fieldEntity.getId(), new ArrayList<>());
|
||||
RuleWithTarget ruleWithTarget = new RuleWithTarget(fieldEntity.getId(), rule, fieldEntity);
|
||||
this.rulesBySources.get(fieldEntity.getId()).add(ruleWithTarget);
|
||||
|
||||
if (!this.rulesByTarget.containsKey(rule.getTarget())) this.rulesByTarget.put(rule.getTarget(), new ArrayList<>());
|
||||
this.rulesByTarget.get(rule.getTarget()).add(ruleWithTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -76,21 +81,24 @@ public class VisibilityServiceImpl implements VisibilityService {
|
|||
if (rule.getSource().equals(key)){
|
||||
Field field = definitionFieldSetItem.getFields().get(key);
|
||||
|
||||
List<RuleWithTarget> rulesForParentKey = this.getChainParentRules(rule);
|
||||
|
||||
boolean parentIsVisible = rulesForParentKey != null && !rulesForParentKey.isEmpty() ? this.isChainParentVisible(rulesForParentKey, definitionFieldSetItem.getFields(), definitionFieldSetItem.getOrdinal()) : true;
|
||||
if (definitionFieldSetItem.getFields().containsKey(rule.getTarget())){ //Rule applies only for current multiple item
|
||||
FieldKey fieldKey = new FieldKey(rule.getTarget(), definitionFieldSetItem.getOrdinal());
|
||||
boolean currentState = this.visibility.getOrDefault(fieldKey, false);
|
||||
this.visibility.put(fieldKey, currentState || this.ruleIsTrue(rule, field));
|
||||
this.visibility.put(fieldKey, parentIsVisible && (currentState || this.ruleIsTrue(rule, field)));
|
||||
} else if (!this.definition.getFieldById(rule.getTarget()).isEmpty() || !this.definition.getFieldSetById(rule.getTarget()).isEmpty()) { //Rule applies to different fieldset, so we apply for all multiple items
|
||||
List<Integer> ordinals = this.getKeyOrdinals(rule.getTarget());
|
||||
for (Integer ordinal : ordinals){
|
||||
FieldKey fieldKey = new FieldKey(rule.getTarget(), ordinal);
|
||||
boolean currentState = this.visibility.getOrDefault(fieldKey, false);
|
||||
this.visibility.put(fieldKey, currentState || this.ruleIsTrue(rule, field));
|
||||
this.visibility.put(fieldKey, parentIsVisible && (currentState || this.ruleIsTrue(rule, field)));
|
||||
}
|
||||
} else {
|
||||
FieldKey fieldKey = new FieldKey(rule.getTarget(), null); //Ordinal is null if target not on field
|
||||
boolean currentState = this.visibility.getOrDefault(fieldKey, false);
|
||||
this.visibility.put(fieldKey, currentState || this.ruleIsTrue(rule, field));
|
||||
this.visibility.put(fieldKey, parentIsVisible && (currentState || this.ruleIsTrue(rule, field)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -103,6 +111,65 @@ public class VisibilityServiceImpl implements VisibilityService {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean isChainParentVisible(List<RuleWithTarget> rulesForParentKey, Map<String, Field> fieldsMap, int ordinal) {
|
||||
boolean isVisible = false;
|
||||
if (rulesForParentKey == null || rulesForParentKey.isEmpty()) return false;
|
||||
|
||||
for (RuleWithTarget ruleForParentKey : rulesForParentKey) {
|
||||
Field field = fieldsMap.get(ruleForParentKey.getSource());
|
||||
|
||||
List<RuleWithTarget> rulesForGrandParentKey = this.getChainParentRules(ruleForParentKey);
|
||||
|
||||
if (fieldsMap.containsKey(ruleForParentKey.getTarget())){ //Rule applies only for current multiple item
|
||||
FieldKey fieldKey = new FieldKey(ruleForParentKey.getTarget(), ordinal);
|
||||
boolean currentState = this.visibility.getOrDefault(fieldKey, false);
|
||||
isVisible = isVisible || currentState || this.ruleIsTrue(ruleForParentKey, field);
|
||||
if (rulesForGrandParentKey != null && !rulesForGrandParentKey.isEmpty()) isVisible = isVisible || this.isChainParentVisible(rulesForGrandParentKey, fieldsMap, ordinal);
|
||||
|
||||
} else if (!this.definition.getFieldById(ruleForParentKey.getTarget()).isEmpty() || !this.definition.getFieldSetById(ruleForParentKey.getTarget()).isEmpty()) { //Rule applies to different fieldset, so we apply for all multiple items
|
||||
List<Integer> ordinals = this.getKeyOrdinals(ruleForParentKey.getTarget());
|
||||
for (Integer curentOrdinal : ordinals){
|
||||
FieldKey fieldKey = new FieldKey(ruleForParentKey.getTarget(), curentOrdinal);
|
||||
boolean currentState = this.visibility.getOrDefault(fieldKey, false);
|
||||
isVisible = isVisible || currentState || this.ruleIsTrue(ruleForParentKey, field);
|
||||
if (rulesForGrandParentKey != null && !rulesForGrandParentKey.isEmpty()) isVisible = isVisible || this.isChainParentVisible(rulesForGrandParentKey, this.getKeyFields(ruleForParentKey.getTarget(), curentOrdinal), ordinal);
|
||||
}
|
||||
} else {
|
||||
FieldKey fieldKey = new FieldKey(ruleForParentKey.getTarget(), null); //Ordinal is null if target not on field
|
||||
boolean currentState = this.visibility.getOrDefault(fieldKey, false);
|
||||
isVisible = isVisible || currentState || this.ruleIsTrue(ruleForParentKey, field);
|
||||
//Nothing to check for grandfather this type of field can not have rules
|
||||
}
|
||||
|
||||
if (isVisible) break;
|
||||
}
|
||||
return isVisible;
|
||||
}
|
||||
|
||||
private List<RuleWithTarget> getChainParentRules(RuleWithTarget rule) {
|
||||
if (rule == null || rule.getSource() == null || rule.getSource().isBlank() || this.rulesByTarget == null) return null;
|
||||
return this.rulesByTarget.containsKey(rule.getSource()) ? this.rulesByTarget.get(rule.getSource()).stream().filter(Objects::nonNull).toList() : new ArrayList<>();
|
||||
}
|
||||
|
||||
private Map<String, Field> getKeyFields(String key, int ordinal){
|
||||
if (this.propertyDefinition.getFieldSets() != null && !this.propertyDefinition.getFieldSets().isEmpty()){
|
||||
for (Map.Entry<String, PropertyDefinitionFieldSet> propertyDefinitionFieldSet: this.propertyDefinition.getFieldSets().entrySet()) {
|
||||
if (propertyDefinitionFieldSet.getKey().equals(key)) return propertyDefinitionFieldSet.getValue().getItems().stream().filter(x-> x.getOrdinal() == ordinal).map(PropertyDefinitionFieldSetItem::getFields).findFirst().orElse(new HashMap<>());
|
||||
|
||||
if (propertyDefinitionFieldSet.getValue() != null && propertyDefinitionFieldSet.getValue().getItems() != null && !propertyDefinitionFieldSet.getValue().getItems().isEmpty()) {
|
||||
for (PropertyDefinitionFieldSetItem definitionFieldSetItem : propertyDefinitionFieldSet.getValue().getItems()) {
|
||||
if (definitionFieldSetItem.getFields() != null && !definitionFieldSetItem.getFields().isEmpty()) {
|
||||
for (String fieldKey : definitionFieldSetItem.getFields().keySet()) {
|
||||
if (fieldKey.equals(key)) return propertyDefinitionFieldSet.getValue().getItems().stream().filter(x-> x.getOrdinal() == ordinal).map(PropertyDefinitionFieldSetItem::getFields).findFirst().orElse(new HashMap<>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
private List<Integer> getKeyOrdinals(String key){
|
||||
if (this.propertyDefinition.getFieldSets() != null && !this.propertyDefinition.getFieldSets().isEmpty()){
|
||||
for (Map.Entry<String, PropertyDefinitionFieldSet> propertyDefinitionFieldSet: this.propertyDefinition.getFieldSets().entrySet()) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { Injectable, booleanAttribute } from '@angular/core';
|
||||
import { AbstractControl } from '@angular/forms';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { DescriptionTemplateDefinition, DescriptionTemplateField, DescriptionTemplateFieldSet, DescriptionTemplatePage, DescriptionTemplateSection } from '@app/core/model/description-template/description-template';
|
||||
|
@ -11,7 +11,8 @@ import { DescriptionTemplateFieldType } from '@app/core/common/enum/description-
|
|||
export class VisibilityRulesService {
|
||||
private form: AbstractControl;
|
||||
private definition: DescriptionTemplateDefinition;
|
||||
private rulesBySources: Map<String, RuleWithTarget[]> ;
|
||||
private rulesBySources: Map<string, RuleWithTarget[]> ;
|
||||
private rulesByTarget: Map<string, RuleWithTarget[]> ;
|
||||
public isVisibleMap: { [key: string]: boolean } = {};
|
||||
private _isVisibleMap: { [key: string]: boolean } = null;
|
||||
|
||||
|
@ -36,6 +37,7 @@ export class VisibilityRulesService {
|
|||
this.allDescriptionTemplateFields = null;
|
||||
this.allDescriptionTemplateFieldSets = null;
|
||||
this.rulesBySources = null;
|
||||
this.rulesByTarget = null;
|
||||
this._isVisibleMap = null;
|
||||
this.calculateVisibility();
|
||||
}
|
||||
|
@ -65,6 +67,7 @@ export class VisibilityRulesService {
|
|||
|
||||
public reloadVisibility() {
|
||||
this.rulesBySources = null;
|
||||
this.rulesByTarget = null;
|
||||
this._isVisibleMap = null;
|
||||
this.calculateVisibility();
|
||||
}
|
||||
|
@ -88,8 +91,9 @@ export class VisibilityRulesService {
|
|||
|
||||
private initRules(){
|
||||
if (this.definition == null || this.form == null) return;
|
||||
if (this.rulesBySources != null) return;
|
||||
if (this.rulesBySources != null && this.rulesByTarget != null) return;
|
||||
this.rulesBySources = new Map();
|
||||
this.rulesByTarget = new Map();
|
||||
|
||||
const fields: DescriptionTemplateField[] = this.getAllDescriptionTemplateDefinitionFields(this.definition);
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
|
@ -100,6 +104,9 @@ export class VisibilityRulesService {
|
|||
if (!this.rulesBySources.has(fieldEntity.id)) this.rulesBySources.set(fieldEntity.id, []);
|
||||
const ruleWithTarget: RuleWithTarget = new RuleWithTarget(fieldEntity.id, rule, fieldEntity);
|
||||
this.rulesBySources.get(fieldEntity.id).push(ruleWithTarget);
|
||||
|
||||
if (!this.rulesByTarget.has(rule.target)) this.rulesByTarget.set(rule.target, []);
|
||||
this.rulesByTarget.get(rule.target).push(ruleWithTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -212,25 +219,25 @@ export class VisibilityRulesService {
|
|||
const fieldsMap = new Map(Object.entries(definitionFieldSetItem.fields));
|
||||
fieldsMap.forEach((field: DescriptionFieldPersist, key: string) => {
|
||||
if (rule.source == key){
|
||||
const rulesForParentKey: RuleWithTarget[] = this.getChainParentRules(rule);
|
||||
|
||||
const parentIsVisible = rulesForParentKey != null && rulesForParentKey.length > 0 ? this.isChainParentVisible(rulesForParentKey, propertyDefinition, fieldsMap, definitionFieldSetItem.ordinal) : true;
|
||||
if (fieldsMap.has(rule.target)){ //Rule applies only for current multiple item
|
||||
const fieldKey = this.buildVisibilityKey(rule.target, definitionFieldSetItem.ordinal);
|
||||
const currentState = this._isVisibleMap[fieldKey] ?? false;
|
||||
this._isVisibleMap[fieldKey] = currentState || this.ruleIsTrue(rule, field);
|
||||
//console.log(fieldKey + " " + this._isVisibleMap[fieldKey] + " " + field.textListValue);
|
||||
this._isVisibleMap[fieldKey] = parentIsVisible && (currentState || this.ruleIsTrue(rule, field));
|
||||
} else if (this.getDescriptionTemplateDefinitionFieldById(this.definition, rule.target).length > 0 || this.getDescriptionTemplateDefinitionFieldSetById(this.definition, rule.target).length > 0) { //Rule applies to different fieldset, so we apply for all multiple items
|
||||
const ordinals: number[] = this.getKeyOrdinals(rule.target, propertyDefinition);
|
||||
for (let k = 0; k < ordinals.length; k++) {
|
||||
const ordinal = ordinals[j];
|
||||
const ordinal = ordinals[k];
|
||||
const fieldKey = this.buildVisibilityKey(rule.target, ordinal);
|
||||
const currentState = this._isVisibleMap[fieldKey] ?? false;
|
||||
this._isVisibleMap[fieldKey] = currentState || this.ruleIsTrue(rule, field);
|
||||
|
||||
this._isVisibleMap[fieldKey] = parentIsVisible && (currentState || this.ruleIsTrue(rule, field));
|
||||
}
|
||||
} else {
|
||||
const fieldKey = this.buildVisibilityKey(rule.target, null); //Ordinal is null if target not on field
|
||||
const currentState = this._isVisibleMap[fieldKey] ?? false;
|
||||
this._isVisibleMap[fieldKey] = currentState || this.ruleIsTrue(rule, field);
|
||||
this._isVisibleMap[fieldKey] = parentIsVisible && (currentState || this.ruleIsTrue(rule, field));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -243,6 +250,48 @@ export class VisibilityRulesService {
|
|||
});
|
||||
}
|
||||
|
||||
private isChainParentVisible(rulesForParentKey: RuleWithTarget[], propertyDefinition: DescriptionPropertyDefinitionPersist, fieldsMap: Map<string, DescriptionFieldPersist>, ordinal: number): boolean {
|
||||
let isVisible = false;
|
||||
if (rulesForParentKey == null || rulesForParentKey.length == 0) return false;
|
||||
|
||||
for (let i = 0; i < rulesForParentKey.length; i++) {
|
||||
const ruleForParentKey = rulesForParentKey[i];
|
||||
const field: DescriptionFieldPersist = fieldsMap.get(ruleForParentKey.source);
|
||||
|
||||
const rulesForGrandParentKey: RuleWithTarget[] = this.getChainParentRules(ruleForParentKey);
|
||||
|
||||
if (fieldsMap.has(ruleForParentKey.target)){ //Rule applies only for current multiple item
|
||||
const fieldKey = this.buildVisibilityKey(ruleForParentKey.target, ordinal);
|
||||
const currentState = this._isVisibleMap[fieldKey] ?? false;
|
||||
isVisible = isVisible || currentState || this.ruleIsTrue(ruleForParentKey, field);
|
||||
if (rulesForGrandParentKey != null && rulesForGrandParentKey.length > 0) isVisible = isVisible || this.isChainParentVisible(rulesForGrandParentKey, propertyDefinition, fieldsMap, ordinal);
|
||||
|
||||
} else if (this.getDescriptionTemplateDefinitionFieldById(this.definition, ruleForParentKey.target).length > 0 || this.getDescriptionTemplateDefinitionFieldSetById(this.definition, ruleForParentKey.target).length > 0) { //Rule applies to different fieldset, so we apply for all multiple items
|
||||
const ordinals: number[] = this.getKeyOrdinals(ruleForParentKey.target, propertyDefinition);
|
||||
for (let k = 0; k < ordinals.length; k++) {
|
||||
const curentOrdinal = ordinals[k];
|
||||
const fieldKey = this.buildVisibilityKey(ruleForParentKey.target, curentOrdinal);
|
||||
const currentState = this._isVisibleMap[fieldKey] ?? false;
|
||||
isVisible = isVisible || currentState || this.ruleIsTrue(ruleForParentKey, field);
|
||||
if (rulesForGrandParentKey != null && rulesForGrandParentKey.length > 0) isVisible = isVisible || this.isChainParentVisible(rulesForGrandParentKey, propertyDefinition, this.getKeyFields(ruleForParentKey.target, curentOrdinal, propertyDefinition), ordinal);
|
||||
}
|
||||
} else {
|
||||
const fieldKey = this.buildVisibilityKey(ruleForParentKey.target, null); //Ordinal is null if target not on field
|
||||
const currentState = this._isVisibleMap[fieldKey] ?? false;
|
||||
isVisible = isVisible || currentState || this.ruleIsTrue(ruleForParentKey, field);
|
||||
//Nothing to check for grandfather this type of field can not have rules
|
||||
}
|
||||
|
||||
if (isVisible) break;
|
||||
}
|
||||
return isVisible;
|
||||
}
|
||||
|
||||
private getChainParentRules(rule: RuleWithTarget) : RuleWithTarget[] {
|
||||
if (!rule?.source || this.rulesByTarget == null) return null;
|
||||
return this.rulesByTarget?.get(rule.source)?.filter(x=> x != null);
|
||||
}
|
||||
|
||||
private getKeyOrdinals(key: string, propertyDefinition: DescriptionPropertyDefinitionPersist): number[]{
|
||||
let ordinals = [];
|
||||
if (propertyDefinition.fieldSets != null) {
|
||||
|
@ -267,6 +316,30 @@ export class VisibilityRulesService {
|
|||
return ordinals;
|
||||
}
|
||||
|
||||
private getKeyFields(key: string, ordinal: number, propertyDefinition: DescriptionPropertyDefinitionPersist): Map<string, DescriptionFieldPersist>{
|
||||
let fields: Map<string, DescriptionFieldPersist>;
|
||||
if (propertyDefinition.fieldSets != null) {
|
||||
new Map(Object.entries(propertyDefinition.fieldSets)).forEach((propertyDefinitionFieldSet: DescriptionPropertyDefinitionFieldSetPersist, propertyDefinitionFieldSetKey: string) => {
|
||||
if (propertyDefinitionFieldSetKey == key) {
|
||||
fields = propertyDefinitionFieldSet.items?.find(x => x.ordinal == ordinal)?.fields;
|
||||
return fields;
|
||||
}
|
||||
if (propertyDefinitionFieldSet.items != null && propertyDefinitionFieldSet.items.length > 0) {
|
||||
for (let i = 0; i < propertyDefinitionFieldSet.items.length; i++) {
|
||||
const definitionFieldSetItem = propertyDefinitionFieldSet.items[i];
|
||||
if (definitionFieldSetItem?.fields != null) {
|
||||
new Map(Object.entries(definitionFieldSetItem.fields)).forEach((field: DescriptionFieldPersist, fieldKey: string) => {
|
||||
if (fieldKey == key) fields = propertyDefinitionFieldSet.items?.find(x => x.ordinal == ordinal)?.fields;
|
||||
});
|
||||
if (fields != null && fields.size > 0) return fields;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return fields ? new Map<string, DescriptionFieldPersist>(Object.entries(fields)) : new Map<string, DescriptionFieldPersist>();
|
||||
}
|
||||
|
||||
private ruleIsTrue(rule: RuleWithTarget, field: DescriptionFieldPersist) :boolean{
|
||||
if (field != null){
|
||||
const fieldType: DescriptionTemplateFieldType = rule.field != null && rule.field.data != null ? rule.field.data.fieldType : DescriptionTemplateFieldType.FREE_TEXT;
|
||||
|
|
Loading…
Reference in New Issue