Visibility rule service refactor.
* Take into account other dependencies * OR and AND logic support
This commit is contained in:
parent
b40e30ee60
commit
d229189783
|
@ -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)=>{
|
||||
|
|
|
@ -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
|
||||
// }
|
||||
|
||||
this.visibilityRulesService.appendVisibilityRule(visRule);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
});
|
||||
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
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.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);
|
||||
}
|
||||
this._emitChangesIfNeeded(visibilityRule.targetControlId, true);
|
||||
this.elementVisibilityMap.set(visibilityRule.targetControlId, true);
|
||||
//this.updateValueAndVisibility(visibilityRule.targetControlId, null);
|
||||
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
||||
if(visibilityValues.length){
|
||||
return visibilityValues.reduce((r, c)=>{
|
||||
if(this.VISIBILITY_RULE_LOGIC === 'OR'){
|
||||
return r || c;
|
||||
} else {
|
||||
return r && c;
|
||||
}
|
||||
}, visibilityValues[0]);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue