Skip to content

Commit

Permalink
feat(core/form): make a slide readonly by condition
Browse files Browse the repository at this point in the history
  • Loading branch information
peppedeka committed Sep 9, 2021
1 parent f2a7a8d commit 6d6f0e4
Show file tree
Hide file tree
Showing 13 changed files with 119 additions and 23 deletions.
2 changes: 1 addition & 1 deletion src/core/forms/field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export abstract class AjfFormField implements OnDestroy, OnInit {
}
});
}
this._updatedSub = this._instance.updatedEvt.subscribe(() => this._cdr.markForCheck());
this._instance.updatedEvt.subscribe(() => this._cdr.markForCheck());
} catch (e) {
console.log(e);
}
Expand Down
70 changes: 54 additions & 16 deletions src/core/forms/form-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ import {nodeInstanceCompleteName} from './utils/nodes-instances/node-instance-co
import {nodeInstanceSuffix} from './utils/nodes-instances/node-instance-suffix';
import {nodeToNodeInstance} from './utils/nodes-instances/node-to-node-instance';
import {updateConditionalBranches} from './utils/nodes-instances/update-conditional-branches';
import {updateEditability} from './utils/nodes-instances/update-editability';
import {updateVisibility} from './utils/nodes-instances/update-visibility';
import {flattenNodes} from './utils/nodes/flatten-nodes';
import {isContainerNode} from './utils/nodes/is-container-node';
Expand Down Expand Up @@ -128,6 +129,10 @@ const updateSlideValidity = (slide: AjfRepeatingSlideInstance|AjfSlideInstance)

@Injectable()
export class AjfFormRendererService {
private _editabilityNodesMap: Observable<AjfRendererUpdateMap>;
private _editabilityNodesMapUpdates: Subject<AjfRendererUpdateMapOperation> =
new Subject<AjfRendererUpdateMapOperation>();

private _visibilityNodesMap: Observable<AjfRendererUpdateMap>;
private _visibilityNodesMapUpdates: Subject<AjfRendererUpdateMapOperation> =
new Subject<AjfRendererUpdateMapOperation>();
Expand Down Expand Up @@ -348,6 +353,17 @@ export class AjfFormRendererService {
}

private _initUpdateMapStreams(): void {
this._editabilityNodesMap =
(<Observable<AjfRendererUpdateMapOperation>>this._editabilityNodesMapUpdates)
.pipe(
scan(
(rmap: AjfRendererUpdateMap, op: AjfRendererUpdateMapOperation) => {
return op(rmap);
},
{}),
startWith({} as AjfRendererUpdateMap),
share(),
);
this._visibilityNodesMap =
(<Observable<AjfRendererUpdateMapOperation>>this._visibilityNodesMapUpdates)
.pipe(
Expand Down Expand Up @@ -449,9 +465,9 @@ export class AjfFormRendererService {
);

this._nodesMaps = [
this._visibilityNodesMap, this._repetitionNodesMap, this._conditionalBranchNodesMap,
this._formulaNodesMap, this._validationNodesMap, this._warningNodesMap,
this._nextSlideConditionsNodesMap, this._filteredChoicesNodesMap,
this._editabilityNodesMap, this._visibilityNodesMap, this._repetitionNodesMap,
this._conditionalBranchNodesMap, this._formulaNodesMap, this._validationNodesMap,
this._warningNodesMap, this._nextSlideConditionsNodesMap, this._filteredChoicesNodesMap,
this._triggerConditionsNodesMap
];
}
Expand Down Expand Up @@ -586,6 +602,9 @@ export class AjfFormRendererService {
}
updateFieldInstanceState(fInstance, context);
}
if (nodeType === AjfNodeType.AjfSlide) {
updateEditability(instance as AjfSlideInstance, context);
}
this._addNodeInstance(instance);
}
return instance;
Expand Down Expand Up @@ -727,28 +746,29 @@ export class AjfFormRendererService {
[any, any], AjfRendererUpdateMap, AjfRendererUpdateMap, AjfRendererUpdateMap,
AjfRendererUpdateMap, AjfRendererUpdateMap, AjfRendererUpdateMap,
AjfRendererUpdateMap, AjfRendererUpdateMap, AjfRendererUpdateMap,
AjfNodeInstance[]
AjfRendererUpdateMap, AjfNodeInstance[]
]>(...(this._nodesMaps), this._flatNodes),
)
.subscribe((v: [
[any, any], AjfRendererUpdateMap, AjfRendererUpdateMap,
AjfRendererUpdateMap, AjfRendererUpdateMap, AjfRendererUpdateMap,
AjfRendererUpdateMap, AjfRendererUpdateMap, AjfRendererUpdateMap,
AjfRendererUpdateMap, AjfNodeInstance[]
AjfRendererUpdateMap, AjfRendererUpdateMap, AjfNodeInstance[]
]) => {
const oldFormValue = init && {} || v[0][0];
init = false;
const newFormValue = v[0][1];
const visibilityMap = v[1];
const repetitionMap = v[2];
const conditionalBranchesMap = v[3];
const formulaMap = v[4];
const validationMap = v[5];
const warningMap = v[6];
const nextSlideConditionsMap = v[7];
const filteredChoicesMap = v[8];
const triggerConditionsMap = v[9];
const nodes = v[10];
const editability = v[1];
const visibilityMap = v[2];
const repetitionMap = v[3];
const conditionalBranchesMap = v[4];
const formulaMap = v[5];
const validationMap = v[6];
const warningMap = v[7];
const nextSlideConditionsMap = v[8];
const filteredChoicesMap = v[9];
const triggerConditionsMap = v[10];
const nodes = v[11];

// takes the names of the fields that have changed
const delta = this._formValueDelta(oldFormValue, newFormValue);
Expand All @@ -763,6 +783,14 @@ export class AjfFormRendererService {
delta.forEach((fieldName) => {
updatedNodes = updatedNodes.concat(
nodes.filter(n => nodeInstanceCompleteName(n) === fieldName));
if (editability[fieldName] != null) {
editability[fieldName].forEach(nodeInstance => {
if (isSlideInstance(nodeInstance)) {
const slideInstance = nodeInstance as AjfSlideInstance;
updateEditability(slideInstance, newFormValue);
}
});
}
if (visibilityMap[fieldName] != null) {
visibilityMap[fieldName].forEach(nodeInstance => {
const completeName = nodeInstanceCompleteName(nodeInstance);
Expand Down Expand Up @@ -1013,6 +1041,7 @@ export class AjfFormRendererService {
this._removeNodesFilteredChoicesMapIndex(nodeName);
this._removeNodesTriggerConditionsMapIndex(nodeName);
if (isSlidesInstance(nodeInstance)) {
this._removeNodesEditabilityMapIndex(nodeName);
return this._removeSlideInstance(nodeInstance as AjfBaseSlideInstance);
} else if (isRepeatingContainerNode(nodeInstance.node)) {
this._removeNodeGroupInstance(nodeInstance as AjfRepeatingContainerNodeInstance);
Expand Down Expand Up @@ -1200,6 +1229,9 @@ export class AjfFormRendererService {

private _addSlideInstance(slideInstance: AjfSlideInstance): AjfSlideInstance {
const slide = slideInstance.node;
if (slide.readonly != null) {
this._addToNodesEditabilityMap(slideInstance, slide.readonly.condition);
}
if (slide.visibility != null) {
this._addToNodesVisibilityMap(slideInstance, slide.visibility.condition);
}
Expand Down Expand Up @@ -1233,7 +1265,9 @@ export class AjfFormRendererService {
}
return nodeGroupInstance;
}

private _removeNodesEditabilityMapIndex(index: string): void {
this._removeNodesMapIndex(this._editabilityNodesMapUpdates, index);
}
private _removeNodesVisibilityMapIndex(index: string): void {
this._removeNodesMapIndex(this._visibilityNodesMapUpdates, index);
}
Expand Down Expand Up @@ -1342,6 +1376,10 @@ export class AjfFormRendererService {
}
}

private _addToNodesEditabilityMap(nodeInstance: AjfNodeInstance, formula: string): void {
this._addToNodesMap(this._editabilityNodesMapUpdates, nodeInstance, formula);
}

private _addToNodesVisibilityMap(nodeInstance: AjfNodeInstance, formula: string): void {
this._addToNodesMap(this._visibilityNodesMapUpdates, nodeInstance, formula);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@
*/

import {AjfNodeInstance} from './node-instance';

export interface AjfContainerNodeInstance extends AjfNodeInstance {
nodes: AjfNodeInstance[];
flatNodes: AjfNodeInstance[];
nodes: AjfNodeInstance[];
}
3 changes: 3 additions & 0 deletions src/core/forms/interface/nodes/container-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@
*
*/

import {AjfCondition} from '@ajf/core/models';
import {AjfNode} from './node';

export interface AjfContainerNode extends AjfNode {
nodes: AjfNode[];
// AjfCondition for handling field writable
readonly?: AjfCondition;
}
4 changes: 3 additions & 1 deletion src/core/forms/interface/slides-instances/slide-instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
* If not, see http://www.gnu.org/licenses/.
*
*/

import {AjfCondition} from '@ajf/core/models';
import {AjfSlide} from '../slides/slide';
import {AjfBaseSlideInstance} from './base-slide-instance';

export interface AjfSlideInstance extends AjfBaseSlideInstance {
editable: boolean;
node: AjfSlide;
readonly: AjfCondition;
}
6 changes: 5 additions & 1 deletion src/core/forms/serializers/node-serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,12 @@ export class AjfNodeSerializer {
attachmentsOrigins,
);
case AjfNodeType.AjfSlide:
const slideObj = obj as AjfNodeCreate & Partial<AjfSlide>;
if (slideObj.readonly) {
slideObj.readonly = AjfConditionSerializer.fromJson(slideObj.readonly);
}
return AjfNodeSerializer._slideFromJson(
obj as AjfNodeCreate & Partial<AjfSlide>,
slideObj as AjfNodeCreate & Partial<AjfSlide>,
choicesOrigins,
attachmentsOrigins,
);
Expand Down
41 changes: 41 additions & 0 deletions src/core/forms/utils/nodes-instances/update-editability.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* @license
* Copyright (C) Gnucoop soc. coop.
*
* This file is part of the Advanced JSON forms (ajf).
*
* Advanced JSON forms (ajf) is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Advanced JSON forms (ajf) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
* General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Advanced JSON forms (ajf).
* If not, see http://www.gnu.org/licenses/.
*
*/

import {AjfCondition, AjfContext, evaluateExpression} from '@ajf/core/models';

import {AjfSlideInstance} from '../../interface/slides-instances/slide-instance';

export function updateEditability(instance: AjfSlideInstance, context: AjfContext): boolean {
if (instance.readonly == null) {
instance.editable = true;
return true;
}
const readOnly: AjfCondition = instance.readonly;

const oldEditability: boolean = instance.editable;
let newEditability: boolean = !evaluateExpression(readOnly.condition, context);
if (newEditability !== instance.editable) {
instance.editable = newEditability;
}

return oldEditability !== newEditability;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
*
*/

import {neverCondition} from '@ajf/core/models';
import {AjfSlideInstance} from '../../interface/slides-instances/slide-instance';
import {createNodeInstance} from '../nodes-instances/create-node-instance';
import {AjfBaseSlideInstanceCreate} from './base';
Expand All @@ -36,5 +37,7 @@ export function createSlideInstance(instance: AjfSlideInstanceCreate): AjfSlideI
flatNodes: [],
valid: false,
position: 0,
editable: instance.editable || true,
readonly: instance.node.readonly || neverCondition()
};
}
2 changes: 2 additions & 0 deletions src/core/forms/utils/slides/create-slide.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
*
*/

import {neverCondition} from '@ajf/core/models';
import {AjfNodeType} from '../../interface/nodes/node-type';
import {AjfSlide} from '../../interface/slides/slide';
import {AjfContainerNodeCreate, createContainerNode} from '../nodes/create-container-node';
Expand All @@ -30,5 +31,6 @@ export function createSlide(nodeGroup: AjfSlideCreate): AjfSlide {
return {
...createContainerNode(nodeGroup),
nodeType: AjfNodeType.AjfSlide,
readonly: nodeGroup.readonly || neverCondition(),
};
}
2 changes: 1 addition & 1 deletion src/ionic/forms/form.html
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ <h2>
></ion-icon>
<ajf-field
[instance]="fieldInstance|ajfAsFieldInstance"
[readonly]="readonly"
[readonly]="readonly || !slideInstance.editable"
>
</ajf-field>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/material/forms/form-read-only.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ const slideForm = {
name: 'slide',
label: 'slide',
conditionalBranches: [{condition: 'true'}],
readonly: [{condition: 'false'}],
nodes: []
}],
} as any;
Expand Down
2 changes: 1 addition & 1 deletion src/material/forms/form.html
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ <h2>
>
<ajf-field
[instance]="fieldInstance|ajfAsFieldInstance"
[readonly]="readonly"
[readonly]="readonly || !slideInstance.editable"
>
</ajf-field>
</div>
Expand Down
3 changes: 3 additions & 0 deletions tools/public_api_guard/core/forms.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ export declare const enum AjfChoicesType {

export interface AjfContainerNode extends AjfNode {
nodes: AjfNode[];
readonly?: AjfCondition;
}

export declare type AjfContainerNodeCreate = AjfNodeCreate & Partial<AjfContainerNode>;
Expand Down Expand Up @@ -673,7 +674,9 @@ export interface AjfSlide extends AjfContainerNode {
}

export interface AjfSlideInstance extends AjfBaseSlideInstance {
editable: boolean;
node: AjfSlide;
readonly: AjfCondition;
}

export interface AjfStringField extends AjfField {
Expand Down

0 comments on commit 6d6f0e4

Please sign in to comment.