Visibility rule service refactor.

* Take into account other dependencies
* OR and AND logic support
This commit is contained in:
Kristian Ntavidi 2021-09-20 21:34:20 +03:00
parent b40e30ee60
commit d229189783
4 changed files with 198 additions and 61 deletions

View File

@ -61,8 +61,8 @@ export class FormCompositeFieldComponent {
(<FormArray>(this.form.get('multiplicityItems'))).removeAt(0);
this.visibilityRulesService.annihilateId(compositeFieldId);
fieldIds.forEach( x => this.visibilityRulesService.annihilateId(x));
this.visibilityRulesService.removeAllIdReferences(compositeFieldId);
fieldIds.forEach( x => this.visibilityRulesService.removeAllIdReferences(x));
}
deleteMultipeFieldFromCompositeFormGroup() {
@ -73,8 +73,8 @@ export class FormCompositeFieldComponent {
const fieldIds = (this.form.get('fields') as FormArray).controls.map(control => control.get('id').value) as string[];
this.visibilityRulesService.annihilateId(currentId);
fieldIds.forEach(x => this.visibilityRulesService.annihilateId(x));
this.visibilityRulesService.removeAllIdReferences(currentId);
fieldIds.forEach(x => this.visibilityRulesService.removeAllIdReferences(x));
(parent as FormArray).removeAt(index);
(parent as FormArray).controls.forEach((control, i)=>{

View File

@ -121,12 +121,26 @@ export class FormSectionComponent implements OnInit, OnChanges {
const newId = idMappings.find(y=> y.old === x.sourceControlId);
return {...x, sourceControlId: newId.new};
});
const visRule: VisibilityRule = {
targetControlId: idMappings.find(x => x.old === element.id).new,
sourceVisibilityRules: updatedRules
}
// const visRule: VisibilityRule = {
// targetControlId: idMappings.find(x => x.old === element.id).new,
// sourceVisibilityRules: updatedRules
// }
const rules = updatedRules.map(x => {
return {
requiredValue: x.sourceControlValue,
sourceField: x.sourceControlId,
targetField: idMappings.find(l=> l.old === element.id).new,
type: ''
} as Rule;
});
rules.forEach(rule =>{
this.visibilityRulesService.addNewRule(rule);
})
this.visibilityRulesService.appendVisibilityRule(visRule);
// this.visibilityRulesService.appendVisibilityRule(visRule);
}
}
@ -146,11 +160,26 @@ export class FormSectionComponent implements OnInit, OnChanges {
return {...x ,sourceControlId: idMappings.find(y => y.old === element.id).new};
});
const visRule: VisibilityRule = {
targetControlId: target,
sourceVisibilityRules: updatedRules
}
this.visibilityRulesService.appendVisibilityRule(visRule);
// const visRule: VisibilityRule = {
// targetControlId: target,
// sourceVisibilityRules: updatedRules
// }
const rules = updatedRules.map(x =>{
return {
requiredValue: x.sourceControlValue,
sourceField: x.sourceControlId,
targetField: target,
type: ''
} as Rule;
})
rules.forEach(rule =>{
this.visibilityRulesService.addNewRule(rule);
})
// this.visibilityRulesService.appendVisibilityRule(visRule);
});

View File

@ -43,7 +43,10 @@ export class DatasetDescriptionComponent extends BaseComponent implements OnInit
}
ngOnInit() {
this.visibilityRulesService.buildVisibilityRules(this.visibilityRules, this.form);
this.tocentries = this.getTocEntries();
const rules_to_append = this._enrichWithMultiplicityRules(this.tocentries);
this.visibilityRulesService.buildVisibilityRules([...this.visibilityRules, ...rules_to_append ], this.form);
// if (this.form) {
// this.form.valueChanges
@ -54,9 +57,7 @@ export class DatasetDescriptionComponent extends BaseComponent implements OnInit
// }
this.visibilityRulesInstance.emit(this.visibilityRulesService);
this.tocentries = this.getTocEntries();
this._enrichWithMultiplicityRules(this.tocentries);
this.hiddenEntriesIds = this._findHiddenEntries(this.tocentries);
@ -96,10 +97,10 @@ export class DatasetDescriptionComponent extends BaseComponent implements OnInit
private _enrichWithMultiplicityRules(tocentries: ToCEntry[]) : void {
private _enrichWithMultiplicityRules(tocentries: ToCEntry[]) : Rule[] {
if (tocentries){
tocentries.forEach(entry => {
if(entry.type === ToCEntryType.Field) return; // * TODO Me to tora implementation den tha ftasei pote edo
return tocentries.map(entry => {
if(entry.type === ToCEntryType.Field) return []; // * TODO Me to tora implementation den tha ftasei pote edo
if(entry.type === ToCEntryType.FieldSet){
@ -109,24 +110,27 @@ export class DatasetDescriptionComponent extends BaseComponent implements OnInit
// * UPDATE KANEI DESTROY TO COMPONENT H NGIF . PITHANOTATA NA XREIASTEI NA TO KANOUME HIDDEN AN THELOUME KATI ALLO
const multiplicity = entry.form.get('multiplicity').value;
if( (multiplicity.max > 1 ) && (multiplicity.min> 0) && (multiplicity.max >= multiplicity.min)){ // has valid multiplicity
this._createAndAppendVisibilityRule(entry);
return this._createAndAppendVisibilityRule(entry);
}
} catch {
}
return
return [];
}
if(entry.subEntries){
this._enrichWithMultiplicityRules(entry.subEntries);
return this._enrichWithMultiplicityRules(entry.subEntries);
}
})
.reduce((r,c)=>{ return [...c, ...r]},[]);
}
return [];
}
private _createAndAppendVisibilityRule(entry: ToCEntry): void{
private _createAndAppendVisibilityRule(entry: ToCEntry): Rule[]{
const rules_to_append = [];
if(entry && (entry.type === ToCEntryType.FieldSet)){
@ -183,7 +187,7 @@ export class DatasetDescriptionComponent extends BaseComponent implements OnInit
const innerDep = original_as_source.filter(x => innerCompositeFieldOriginalIds.includes(x.targetField));
innerDep.forEach(x =>{
const newRule = {...x, sourceField: field.id, targetField: idMappings.find(l => l.original === x.targetField).multiplicityIdValue} as Rule;
this.visibilityRulesService.addNewRule(newRule);
rules_to_append.push(newRule);
})
@ -191,7 +195,7 @@ export class DatasetDescriptionComponent extends BaseComponent implements OnInit
const outerDep = original_as_source.filter(x => !innerCompositeFieldOriginalIds.includes(x.targetField));
outerDep.forEach(x =>{
const newRule = {...x, sourceField: field.id};
this.visibilityRulesService.addNewRule(newRule);
rules_to_append.push(newRule);
})
}
@ -203,14 +207,14 @@ export class DatasetDescriptionComponent extends BaseComponent implements OnInit
const innerDep = original_as_target.filter( x=> innerCompositeFieldOriginalIds.includes(x.sourceField));
innerDep.forEach(x =>{
const newRule = {...x, targetField: field.id, sourceField: idMappings.find(l => l.original === x.sourceField).multiplicityIdValue} as Rule;
this.visibilityRulesService.addNewRule(newRule);
rules_to_append.push(newRule);
})
//outer dependencies
const outerDep = original_as_target.filter( x=> !innerCompositeFieldOriginalIds.includes(x.sourceField));
outerDep.forEach(x=>{
const newRule = {...x, targetField: field.id} as Rule;
this.visibilityRulesService.addNewRule(newRule);
rules_to_append.push(newRule);
})
}
@ -238,13 +242,15 @@ export class DatasetDescriptionComponent extends BaseComponent implements OnInit
compositeFieldAsTargetRules.forEach(x =>{
idCompositeFieldMappings.forEach(l=>{
const newRule = {...x, targetField: l.newValue};
this.visibilityRulesService.addNewRule(newRule);
rules_to_append.push(newRule);
});
});
}
}
return rules_to_append;
}
private _buildRecursively(form: FormGroup,whatAmI:ToCEntryType):ToCEntry{

View File

@ -12,10 +12,12 @@ import { VisibilityRulesContext } from './models/visibility-rules-context';
export class VisibilityRulesService {
private readonly VISIBILITY_RULE_LOGIC: 'OR'| 'AND' = 'OR';
private readonly DEFAULTVISIBILITY = false;
private visibilityRuleContext: VisibilityRulesContext;
private form: AbstractControl;
private elementVisibilityMap = new Map<String, boolean>();
private elementComputationalMap = new Map<String, Map<String,boolean>>(); /// keep saved the values of each form control validity value
private _changeMade$ = new Subject<void>();
@ -40,10 +42,14 @@ export class VisibilityRulesService {
public updateValueAndVisibility(id: string, value: any) {
const visibilityRules = this.visibilityRuleContext.rules.filter(item => item.sourceVisibilityRules.filter(source => source.sourceControlId === id).length > 0);
visibilityRules.forEach(item => this.evaluateVisibility(item, value));
visibilityRules.forEach(item => this.evaluateVisibility(item, value, id));
}
private evaluateVisibility(visibilityRule: VisibilityRule, value: any) {
private evaluateVisibility(visibilityRule: VisibilityRule, value: any, sourceId: string) {// source controlId is the same
const targetId = visibilityRule.targetControlId;
const visibilityMap = this.elementComputationalMap.get(targetId)? this.elementComputationalMap.get(targetId): new Map<String, boolean>();
if (value instanceof Array){
@ -53,35 +59,97 @@ export class VisibilityRulesService {
const isVisible = parsedValues.map(v=>parsedSourceControlValues.includes(v)).reduce((acc,current)=> acc|| current, false);
if(isVisible){
this._emitChangesIfNeeded(visibilityRule.targetControlId, true);
this.elementVisibilityMap.set(visibilityRule.targetControlId, true);
return;
}
// if(isVisible){
// this._emitChangesIfNeeded(visibilityRule.targetControlId, true);
// this.elementVisibilityMap.set(visibilityRule.targetControlId, true);
// return;
// }
visibilityMap.set(sourceId, isVisible);
} else {
const visibilityDependencySource = visibilityRule.sourceVisibilityRules.filter( x=> x.sourceControlId === sourceId);
visibilityDependencySource.forEach(x => {
const shouldBeHidden = value !== null && (this.parseValue(value) !== this.parseValue(x.sourceControlValue));
// if(value !== null && )
visibilityMap.set(sourceId, !shouldBeHidden);
});
}
this.elementComputationalMap.set(targetId, visibilityMap);// unnessecary
const isVisible = this._computeVisibility(targetId);
this._emitChangesIfNeeded(targetId, isVisible);
this.elementVisibilityMap.set(targetId, isVisible);
if(!isVisible){
this.resetControlWithId(this.form, targetId);
}
// for (let i = 0; i < visibilityRule.sourceVisibilityRules.length; i++) {
// if (value != null && (this.parseValue(value) !== this.parseValue(visibilityRule.sourceVisibilityRules[i].sourceControlValue))) {
// this._emitChangesIfNeeded(visibilityRule.targetControlId, false);
// this.elementVisibilityMap.set(visibilityRule.targetControlId, false);
// this.resetControlWithId(this.form, visibilityRule.targetControlId);
// //this.updateValueAndVisibility(visibilityRule.targetControlId, null);
// // this.clearValues(targetPathKey);
// return;
// }
// }
// this._emitChangesIfNeeded(visibilityRule.targetControlId, true);
// this.elementVisibilityMap.set(visibilityRule.targetControlId, true);
// this.updateValueAndVisibility(visibilityRule.targetControlId, null);
}
private _computeVisibility(targetId: string) : boolean{
const visibilityMap = this.elementComputationalMap.get(targetId);
const values = visibilityMap.values();
let currentVal = values.next();
let visibilityValues: boolean[] = [];
while(!currentVal.done){
visibilityValues.push(currentVal.value);
currentVal = values.next();
}
for (let i = 0; i < visibilityRule.sourceVisibilityRules.length; i++) {
if (value != null && (this.parseValue(value) !== this.parseValue(visibilityRule.sourceVisibilityRules[i].sourceControlValue))) {
this._emitChangesIfNeeded(visibilityRule.targetControlId, false);
this.elementVisibilityMap.set(visibilityRule.targetControlId, false);
this.resetControlWithId(this.form, visibilityRule.targetControlId);
//this.updateValueAndVisibility(visibilityRule.targetControlId, null);
// this.clearValues(targetPathKey);
return;
}
if(visibilityValues.length){
return visibilityValues.reduce((r, c)=>{
if(this.VISIBILITY_RULE_LOGIC === 'OR'){
return r || c;
} else {
return r && c;
}
}, visibilityValues[0]);
}
this._emitChangesIfNeeded(visibilityRule.targetControlId, true);
this.elementVisibilityMap.set(visibilityRule.targetControlId, true);
//this.updateValueAndVisibility(visibilityRule.targetControlId, null);
return this.DEFAULTVISIBILITY;
}
private resetVisibilityRules() {
this.elementVisibilityMap.clear();
this.elementVisibilityMap = new Map<String, boolean>();
this.elementComputationalMap.clear();
this.elementComputationalMap = new Map<String, Map<String, boolean>>();
this._populateComputationMap(); /// !IMPORTANT FOR THE AND LOGIC
this._changeMade$.next();
}
private _populateComputationMap(): void{
this.visibilityRuleContext.rules.forEach(rule =>{
const targetId = rule.targetControlId;
const visibilityMap = this.elementComputationalMap.get(targetId)? this.elementComputationalMap.get(targetId) : new Map< String, boolean>();
rule.sourceVisibilityRules.forEach(vr =>{
visibilityMap.set(vr.sourceControlId, this.DEFAULTVISIBILITY);
});
this.elementComputationalMap.set(targetId, visibilityMap);
});
}
parseValue(value: any) {
if (typeof value === 'string') {
if (isNumeric(value)) { return value; }
@ -195,24 +263,26 @@ export class VisibilityRulesService {
}).map(x => x.targetControlId);
}
public appendVisibilityRule(rule: VisibilityRule): void{
// public appendVisibilityRule(rule: VisibilityRule): void{
const existingTargetRule = this.visibilityRuleContext.rules.find( r => r.targetControlId === rule.targetControlId);
// const existingTargetRule = this.visibilityRuleContext.rules.find( r => r.targetControlId === rule.targetControlId);
if(existingTargetRule){
rule.sourceVisibilityRules.forEach(svr =>{
existingTargetRule.sourceVisibilityRules.push(svr);
});
}else{
this.visibilityRuleContext.rules.push(rule);
}
// if(existingTargetRule){
// rule.sourceVisibilityRules.forEach(svr =>{
// existingTargetRule.sourceVisibilityRules.push(svr);
// });
// }else{
// this.visibilityRuleContext.rules.push(rule);
// }
}
// }
//removes rule that has the specific id either as a source either as a target
public annihilateId(id: string) : void{
public removeAllIdReferences(id: string) : void{
// * Remove from visibility rues and visibility rules context
//remove as a target
const temp = this.visibilityRuleContext.rules.map((x,i) => (x.targetControlId === id )? i : null );
@ -238,10 +308,42 @@ export class VisibilityRulesService {
tbd.reverse().forEach(index =>{
this.visibilityRuleContext.rules.splice(index,1);
});
// * Remove from computational map
// as a target
if(this.elementComputationalMap.get(id)){
this.elementComputationalMap.delete(id);
}
// as a source
const keyIterator = this.elementComputationalMap.keys();
let currentKey = keyIterator.next();
while(!currentKey.done){
const currentVals = this.elementComputationalMap.get(currentKey.value);
currentVals.delete(id);
currentKey = keyIterator.next();
}
}
public addNewRule(rule: Rule): void{
const targetId = rule.targetField;
const sourceId = rule.sourceField;
this.visibilityRuleContext.addToVisibilityRulesContext(rule);
const visibilityMap = this.elementComputationalMap.get(targetId) ? this.elementComputationalMap.get(targetId) : new Map< String, boolean>();
visibilityMap.set(sourceId, this.DEFAULTVISIBILITY);
const isVisible = this._computeVisibility(targetId);
this._emitChangesIfNeeded(targetId, isVisible);
this.elementVisibilityMap.set(targetId, isVisible);
}
}