# Label conditional on value of Select

**URL:** <https://jsonforms.discourse.group/t/label-conditional-on-value-of-select/33>\
**Category:** General\
**Created:** [January 8, 2021, 9:15am UTC](https://jsonforms.discourse.group/t/label-conditional-on-value-of-select/33 "2021-01-08T09:15:11Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![discobot](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/discobot/32/3_2.png) [@discobot](https://jsonforms.discourse.group/u/discobot)\
**Post date:** [January 8, 2021, 9:15am UTC](https://jsonforms.discourse.group/t/label-conditional-on-value-of-select/33/1 "2021-01-08T09:15:11Z")

</div>

I’m trying to render a label that has a value which depends on the selection made in another control.  
Basically I have the following in schema:

“designwithPrice”:{  
“type”: “string”,  
“title”: “Designüberarbeitung”,  
“description”: “Für einen Aufpreis können Sie individuelle Modifikationen am Design machen lassen.”,  
“oneOf”: [  
{ “const”: “none”, “title”: “Keine Überarbeitung”, “description”:“Keine Anpassung”, “price” : 0},  
{ “const”: “M”, “title”: “Kleinere Überarbeitungen”, “price” : 500},  
{ “const”: “L”, “title”: “Deutliche Überarbeitungen”, “price” : 1000},  
{ “const”: “XL”, “title”: “Weitgehende Überarbeitungen”, “price” : 1750}  
]  
},  
“designPrice”:{  
“type” : “string”  
}

and corresponding in uischema:  
{  
“type”: “Group”,  
“label”: “Design”,  
“elements”: [  
{  
“type”: “Control”,  
“scope”: “#/properties/designwithPrice”  
}, {  
“type”: “Label”,  
“text”: “Keine Mehrkosten”  
}  
]  
}

I want the Label to display the value of the price property when the designwithPrice control changes.  
What I’m missing:

- The correct way to trigger onChange inside a custom renderer. Since a renderer isnt quite the same as a react component componentWillUpdate and similar methods don’t work.

- A way for a label element to refer to a value from the data object. (Just like controls are bound to data via scope)

I think that I’m probably messing up the approach, mixing schema and data.

_[[original thread](https://spectrum.chat/jsonforms/general/label-conditional-on-value-of-select~1298150c-9144-4d59-ae04-5e1b3e03e290) by Christian Nyffenegger]_

---

<div class="post-metadata">

**Author:** ![sdirix](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/sdirix/32/1073_2.png) [@sdirix](https://jsonforms.discourse.group/u/sdirix)\
**Post date:** [January 8, 2021, 10:17am UTC](https://jsonforms.discourse.group/t/label-conditional-on-value-of-select/33/2 "2021-01-08T10:17:28Z")

</div>

> I want the Label to display the value of the price property when the designwithPrice control changes.

You can always manually access the whole JSON Forms context and look up the data yourself with `useJsonForms`, i.e. `const jsonforms = useJsonForms()`. For example the data is located in `jsonforms.core.data`. By using this context the component will rerender, whenever the context content changes, i.e. also whenever the contained data changes.

> The correct way to trigger onChange inside a custom renderer. Since a renderer isnt quite the same as a react component componentWillUpdate and similar methods don’t work.

As mentioned above: By using the context the renderer will automatically rerender (this is also how all other renderers update, the context usage is just “hidden” away in `withJsonFormsXYZProps`). Also custom renderers are really just React components and nothing more.

> A way for a label element to refer to a value from the data object. (Just like controls are bound to data via scope)

You can customize your uischema in whichever what you want, so if you like to you can also add a scope to your `Label`s. We recommend adding all customizations into an `options` object, but you don’t necessarily need to. Then you can just use our utility functions to resolve from the scope to the data. Bonus points when doing it in your own custom HOC. This is roughly the code you need to manually resolve a scope defined in a `options` object.

```auto
  const jsonforms = useJsonForms();
  const rootData = jsonforms.core.data;
  const currentPath = composeWithUi(uischema.options.scope, path);
  const scopedData = Resolve.data(rootData, currentPath);

```

This will take a scope placed in the `options` of the Label and resolve it. `path` and `uischema` come in via the props.

> I think that I’m probably messing up the approach, mixing schema and data.

Maybe, I don’t know enough about your use case to give a proper opinion. Adding the custom `price` to the `oneOf` seems mostly to be fine. What exactly is `designPrice`? Also what is the purpose of the extra `Label` in the ui schema? Shouldn’t this just be a part of a custom renderer for the `designwithPrice` control?

---

<div class="post-metadata">

**Author:** ![discobot](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/discobot/32/3_2.png) [@discobot](https://jsonforms.discourse.group/u/discobot)\
**Post date:** [January 11, 2021, 8:57am UTC](https://jsonforms.discourse.group/t/label-conditional-on-value-of-select/33/3 "2021-01-11T08:57:43Z")

</div>

_[Christian Nyffenegger]_

designPrice is a hidden control, which holds the price value of the selected design, since the data object only contains the selected value of designPrice. (e.g. none/“M”,“L”,“XL”)  
I have a working solution right now, but it feels very hacky.

uischema:

```auto
{
    "type": "Group",
    "label": "Design",
    "elements": [
        { 
            "type": "Control", 
            "scope": "#/properties/design",
            "options": {
                "withPrice": true
            }
        }, {
            "type": "Control",
            "scope": "#/properties/designPrice",
            "rule": {
                "effect": "HIDE",
                "condition": {
                    "scope": "#/properties/trueBoolean",
                    "schema":{
                        "const": true
                    }
                }
            }
        }
    ]
},

```

control:

```auto
export const SelectWithPriceControl = (props: ControlProps & OwnPropsOfEnum) => {
    // access the jsonforms object to get schemas and data
    const jsonforms = useJsonForms();    
    const schema = jsonforms.core.schema;
    // determine the path of the control
    const path = props.path;
    const control = schema.properties[path];
    const oneOf = control.oneOf;
    // extend schema to include pricePath property
    type JsonSchemaWithPricePath = JsonSchema & {pricePath: string}; 
    const SchemaWithPricePath = props.schema as JsonSchemaWithPricePath;
    const pricePath = SchemaWithPricePath.pricePath;
    // loop all properties to see which one was selected, then assign the price to our hidden property
    oneOf.forEach(option => {
        if(option.const === jsonforms.core.data[path]){
            jsonforms.core.data[pricePath] = option.price;
        }
    });
    // output the hidden property inside the control
    return (
        <Grid container >
            <Grid item sm={6}>
                <MaterialOneOfEnumControl { ...props} />
            </Grid>
            <Grid item sm={6}>
                { jsonforms.core.data[pricePath] }
            </Grid>
        </Grid>
        );
};

```

---

<div class="post-metadata">

**Author:** ![discobot](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/discobot/32/3_2.png) [@discobot](https://jsonforms.discourse.group/u/discobot)\
**Post date:** [January 11, 2021, 9:14am UTC](https://jsonforms.discourse.group/t/label-conditional-on-value-of-select/33/4 "2021-01-11T09:14:32Z")

</div>

_[Christian Nyffenegger]_

Actually, I was now able to eliminate the designPrice control, since the jsonforms.core.data object does expose all properties, as opposed to the data prop a control receives

Well, actually it doesn’t, but i can access the values from the schema as im doing above.

---

<div class="post-metadata">

**Author:** ![sdirix](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/sdirix/32/1073_2.png) [@sdirix](https://jsonforms.discourse.group/u/sdirix)\
**Post date:** [January 11, 2021, 10:09am UTC](https://jsonforms.discourse.group/t/label-conditional-on-value-of-select/33/5 "2021-01-11T10:09:23Z")

</div>

Hi @christian-nyffenegger, you are right, this looks very hacky. I’ll comment on the details and then give more general advice.

```auto
        "rule": {
          "effect": "HIDE",
          "condition": {
               "scope": "#/properties/trueBoolean",
               "schema":{
                   "const": true
               }
           }
        }

```

You don’t need a “real” boolean here. The only important part is that the schema is successfully validating, so

```auto
"scope": "#",
"schema": {}

```

would always be true and therefore good enough. However in general I’m not a fan of hidden fields. So whenever this is needed I would look for another way to achieve the desired functionality.

- The schema, i.e. control is manually resolved in your renderer. However `props` should already contain the resolved control, i.e. `props.schema`.

- `jsonforms.core.data[pricePath] = option.price;`. You’re not allowed to modify any value of `jsonforms` directly. The content of `jsonforms` is contained in a [React Context](https://reactjs.org/docs/context.html). By modifying it directly the update mechanism of React is bypassed, leading to weird behavior. For updating the `core.data` you can use the `dispatch` in which you can send an `Actions.update` event.

- The `core.data` object has the whole data. When it doesn’t then because of the broken update mechanism described above.

Generally speaking I would avoid a separate “whole price” field, or at least I would avoid updating it from renderers directly.

- Strictly speaking there is no need for a “whole price” field. It’s not really an input but just a derived value, i.e. the price can be calculated from the data object in combination with the schema. So you could move the logic completely outside of JSON Forms, listen to the data changes in JSON Forms, and then update the UI with the calculated price. This is logic you’ll need to implement on the server side anyway, as you can never trust the sent price data. Of course you can also render the value within JSON Forms if you want to, for this I would add a new UI Schema element `TotalPrice` (without a scope, as we don’t model the price) with a custom renderer containing the price calculation logic.

- For redundancy and UX reasons you can of course still calculate and save the value on the client and send it to the server to check whether they agree. I would do it outside of JSON Forms. However if the price shall be rendered within JSON Forms then you could use a regular control (with `readonly: true`) and outside of JSON Forms you still listen to the data changes and update the `data` with the newly calculated price (make sure to construct a new `data` object, otherwise JSON Forms will not update correctly). Once [#1670](https://github.com/eclipsesource/jsonforms/issues/1670) is implemented the calculated price update could also happen within JSON Forms.

So in summary I would avoid incrementing and decrementing the total value within the renderers but just always cleanly calculate it from the data. This can be hard coded when your schema is always the same or dynamically derived from the data + schema with price annotations.

---

<div class="post-metadata">

**Author:** ![discobot](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/discobot/32/3_2.png) [@discobot](https://jsonforms.discourse.group/u/discobot)\
**Post date:** [February 3, 2021, 8:25am UTC](https://jsonforms.discourse.group/t/label-conditional-on-value-of-select/33/6 "2021-02-03T08:25:23Z")

</div>

_[Christian Nyffenegger]_

I’ve tried reading up on dispatch, but as far as I can tell that is a redux concept? I’m trying to code exclusively in React, because a) I know nothing about Redux b) it’s deprecated by JsonForms.

I’ve been able to use the onChange property on the JsonForms to set state. But now I need to capture a click event from a custom component which **doesn’t** change any data. Or is the solution for that to just change any data inside the custom component?

---

<div class="post-metadata">

**Author:** ![discobot](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/discobot/32/3_2.png) [@discobot](https://jsonforms.discourse.group/u/discobot)\
**Post date:** [February 3, 2021, 8:44am UTC](https://jsonforms.discourse.group/t/label-conditional-on-value-of-select/33/7 "2021-02-03T08:44:14Z")

</div>

_[Christian Nyffenegger]_

(Specificially I used it to change a custom state for our App, not state in the JsonForms object)

---

<div class="post-metadata">

**Author:** ![sdirix](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/sdirix/32/1073_2.png) [@sdirix](https://jsonforms.discourse.group/u/sdirix)\
**Post date:** [February 3, 2021, 9:11am UTC](https://jsonforms.discourse.group/t/label-conditional-on-value-of-select/33/8 "2021-02-03T09:11:54Z")

</div>

> I’ve tried reading up on dispatch, but as far as I can tell that is a redux concept? I’m trying to code exclusively in React, because a) I know nothing about Redux b) it’s deprecated by JsonForms.

Reducers with dispatch is [a core feature of React](https://reactjs.org/docs/hooks-reference.html#usereducer). It works similar as the one of Redux and therefore also uses the same naming. We used it to get rid of Redux. So while there is no more Redux involved, there is still a dispatch available.

> I’ve been able to use the onChange property on the JsonForms to set state. But now I need to capture a click event from a custom component which doesn’t change any data. Or is the solution for that to just change any data inside the custom component?

You can solve this via [React context](https://reactjs.org/docs/context.html). You create a context outside of JSON Forms, consume it in your custom renderer and then call some callback which you placed in your context. Your app can then react on the callback and do whatever you want.
