Update data in custom renderer

Hey folks, I started using JSONforms recently and got an interesting question.
I have an Angular app and use “@jsonforms/angular”: “3.2.0”, “@jsonforms/angular-material”: “3.2.0”, “@jsonforms/core”: “3.2.0”.
I need to have such functionality: two checkboxes - Enable and Required, when the Required is checked, Enable should be checked as well, and the same behaviour when Required is unchecked. The important thing is that I have to send a string (required or enabled), not a boolean.
I couldn’t find a normal way to do it, so I created a custom renderer where I added the two checkboxes like this
<input
type=“checkbox”
name=“checkbox_{{id}}”
#enabledCheckbox
(change)=“onCheckboxChanged(FORM_FIELD_STATE.ENABLED)”
[id]=“id”
[disabled]=“!isEnabled()”/>

export class FormFieldsComponent extends ToggleControlRenderer {
@ViewChild(‘enabledCheckbox’, {static: true}) enabledCheckbox: ElementRef;
@ViewChild(‘requiredCheckbox’, {static: true}) requiredCheckbox: ElementRef;

readonly FORM_FIELD_STATE = FORM_FIELD_STATE;

constructor(
private jsonformsService: JsonFormsAngularService,
changeDetectorRef: ChangeDetectorRef
) {
super(jsonformsService, changeDetectorRef);
}

ngOnInit() {
super.ngOnInit();

if (this.data === FORM_FIELD_STATE.ENABLED) {
  this.enabledCheckbox.nativeElement.checked = true;
  this.form.setValue(FORM_FIELD_STATE.ENABLED);
}
if (this.data === FORM_FIELD_STATE.REQUIRED) {
  this.enabledCheckbox.nativeElement.checked = true;
  this.requiredCheckbox.nativeElement.checked = true;
  this.form.setValue(FORM_FIELD_STATE.REQUIRED);
}

}

onCheckboxChanged(state: FORM_FIELD_STATE) {
if (state === FORM_FIELD_STATE.REQUIRED) {
this.enabledCheckbox.nativeElement.checked = this.requiredCheckbox.nativeElement.checked;
}
this.data = FORM_FIELD_STATE.ENABLED;
this.form.setValue(FORM_FIELD_STATE.ENABLED);
this.jsonformsService.updateCore(
Actions.update(this.propsPath, () => FORM_FIELD_STATE.ENABLED)
);
this.jsonformsService.setData(FORM_FIELD_STATE.ENABLED);
}
}

I just hardcoded ‘enabled’ to check if the data will be updated, but it doesn’t update.
How can I update the data from the Custom Renderer?
Thanks in advance

Schema:
{
“definitions”: {
“order”: {
“type”: “object”,
“properties”: {
“customer”: {
“type”: “object”,
“properties”: {
“id”: { “type”: “string” },
“name”: { “type”: “string”},
“department”: {
“type”: “string”,
“enum”: [“Complaints Division”, “Complaints Division 2”, “Complaints Division 3”]
},
“emailAddress”: { “type”: “string”, “format”: “email” }
}
},
“title”: {
“type”: “string”,
“minLength”: 5
},
“description”: {
“type”: “string”
},
“ordered”: { “type”: “string” },
“processId”: {
“type”: “number”,
“minimum”: 0
},
“assignee”: { “type”: “string” },
“startDate”: {
“type”: “string”,
“format”: “date”
},
“endDate”: {
“type”: “string”,
“format”: “date”
},
“status”: {
“type”: “string”,
“enum”: [“unordered”, “planned”, “ordered”]
},
“amount”: {
“type”: “integer”,
“minimum”: 1,
“maximum”: 100,
“default”: 10,
“multipleOf”: 1
}
}
}
},
“type”: “object”,
“properties”: {
“orders”: {
“type”: “array”,
“items”: {
“$ref”: “#/definitions/order”
}
}
},
“required”: [“title”]
}

UI Schema:
{
“type”: “Categorization”,
“elements”: [
{
“type”: “Category”,
“label”: “Orders”,
“elements”: [
{
“type”: “ListWithDetail”,
“scope”: “#/properties/orders”,
“options”: {
“labelRef”: “#/items/properties/customer/properties/name”,
“detail”: {
“type”: “VerticalLayout”,
“elements”: [
{
“type”: “HorizontalLayout”,
“elements”: [
{
“type”: “Control”,
“scope”: “#/properties/title”
},
{
“type”: “Control”,
“scope”: “#/properties/processId”
}
]
},
{
“type”: “VerticalLayout”,
“elements”: [
{
“type”: “VerticalLayout”,
“elements”: [
{

                      "type": "Control",
                      "scope": "#/properties/assignee"
                    },
                    {
                      "type": "HorizontalLayout",
                      "elements": [
                        {
                          "type": "Control",
                          "scope": "#/properties/startDate"
                        },
                        {
                          "type": "Control",
                          "scope": "#/properties/endDate"
                        }
                      ]
                    },
                    {
                      "type": "Control",
                      "scope": "#/properties/status"
                    },
                    {
                      "type": "Control",
                      "scope": "#/properties/ordered",
                      "options": {
                        "toggle": true
                      }
                    },
                    {
                      "type": "Control",
                      "scope": "#/properties/amount",
                      "options": {
                        "slider": true
                      },
                      "rule": {
                        "effect": "DISABLE",
                        "condition": {
                          "schema": {
                            "const": "unordered"
                          },
                          "scope": "#/properties/status"
                        }
                      }
                    }

                  ]
                }
              ]
            },
            {
              "type": "Group",
              "label": "Customer",
              "elements": [
                {
                  "type": "Control",
                  "scope": "#/properties/customer/properties/name"
                },
                {
                  "type": "Control",
                  "scope": "#/properties/customer/properties/department"
                },
                {
                  "type": "Control",
                  "scope": "#/properties/customer/properties/emailAddress"
                }
              ]
            },
            {
              "type": "Control",
              "scope": "#/properties/description",
              "options": {
                "multi": true
              }
            }
          ]
        }
      }
    }
  ]
},
{
  "type": "Category",
  "label": "Data",
  "elements": [
    {
      "type": "Label",
      "text": "Data"
    },
    {
      "type": "Control",
      "scope": "#/___data"
    }
  ]
},
{
  "type": "Category",
  "label": "Language",
  "elements": [
    {
      "type": "Label",
      "text": "Choose preferred locale"
    },
    {
      "type": "Control",
      "scope": "#",
      "options": {
        "lang": true
      }
    }
  ]
},
{
  "type": "Category",
  "label": "Contracts",
  "elements": [
    {
      "type": "Label",
      "text": "Currently Empty Category"
    }
  ]
}

]
}

Hi @VladislavLinnik,

I see you tried all kinds of updating the JSON Forms State. This one should be sufficient:

this.jsonformsService.updateCore(
  Actions.update(this.propsPath, () => FORM_FIELD_STATE.ENABLED)
);

However I’m missing some context here: To which property in the posted schema does your custom renderer belong? I don’t see any enable or required property there. Can you post the tester of the custom renderer?

  • Note that in general I would not recommend extending ToggleControlRenderer for your use case but just extending JsonFormsControl.
    I couldn’t find a normal way to do it, so I created a custom renderer where I added the two checkboxes
  • In case your custom renderer should adapt two properties of the schema at the same time, then this is possible but is quite cumbersome as this is not supported out of the box. You basically have to re-implement JsonFormsControl and bind against two properties instead of one

When having such dependent use cases, it’s easier to implement this via middleware.


In summary:

  • If you like to render a text field as a toggle, then implement a custom renderer extending from JsonFormsControl, setting the text property as you need
  • To adapt properties based on changes on other properties, I recommend using middleware.

Many thanks, Stefan for the recommendations. I’ll try