Reset field after field has been set to invisible

Description of the limitation and why it is relevant to address

As a VIKTOR app developer, I would like the fields that are not visible to be empty or reset, so that other fields are not influenced by invisible fields.

I think this is relevant for the VIKTOR platform because it will decrease the number of statements needed in visible, increase the readability of the code and avoid unwanted visibility of fields.

Example which shows the problem:

I have field_1 (What do you want on your bread?) with options Cheese and Peanut butter.
I have field_2 (What kind of Cheese do you want?) with options Gouda or Beemster, and I only want to show this if field_1 == Cheese.
Then I have field_3 (Do you want 30+ or 48+ Gouda Cheese), and I only want to show this if field_2 == Gouda.

The problem is, when in a previous attempt in the same session, field_2 is set to Gouda, but field_1 is now equal to Peanut butter (which makes field_2 invisible), it will still show field_3 even though it is not logical to show this. This can be solved by using both statements, but this can become quite messy.

Submitter proposed design (optional)

I would suggest making the field empty/reset the field when it becomes invisible.

Current workarounds

Use multiple statements, which can become quite a mess.
For the example (if field_3 is a NumberField):

field_3 = NumberField('field_3', visible = And(IsEqual(Lookup('field_1'), 'Cheese'), IsEqual(Lookup('field_2'), 'Gouda')))

In this case it only includes two statements, but I already have some cases where I use 4 or 5.

Hi Marieke,

First of all, thanks for the wonderful description! To understand your case, I retyped your example to some code that can be applied in a VIKTOR app:

from viktor.parametrization import ViktorParametrization, NumberField, OptionField, IsEqual, Lookup, And


class Parametrization(ViktorParametrization):
    field_1 = OptionField('Cheese or Peanut butter', options=['Cheese', 'Peanut butter'])
    field_2 = OptionField("What kind of Cheese do you want?", options=['Gouda', 'Beemster'], visible=IsEqual(Lookup('field_1'), 'Cheese'))
    field_3 = NumberField("Do you want 30 + or 48 + Gouda Cheese", visible=And(IsEqual(Lookup('field_1'), 'Cheese'), IsEqual(Lookup('field_2'), 'Gouda')))

Just to check, do you know you can use a callback function to define the visibility as well? Here is an example:

from viktor.parametrization import ViktorParametrization, NumberField, OptionField


def is_cheese(params, **kwargs):
    return params.field_1 == 'Cheese'


def is_gouda(params, **kwargs):
    if is_cheese(params):
        return params.field_2 == 'Gouda'
    return False


class Parametrization(ViktorParametrization):
    field_1 = OptionField('Cheese or Peanut butter', options=['Cheese', 'Peanut butter'])
    field_2 = OptionField("What kind of Cheese do you want?", options=['Gouda', 'Beemster'], visible=is_cheese)
    field_3 = NumberField("Do you want 30 + or 48 + Gouda Cheese", visible=is_gouda)

It is a bit more lengthy, but much clearer to read.

Another approach would be to add your statements to clearly defined variables, which makes the code look even more readable for this example:

from viktor.parametrization import ViktorParametrization, NumberField, OptionField, IsEqual, Lookup, And


is_cheese = IsEqual(Lookup('field_1'), 'Cheese')
is_gouda = And(is_cheese, IsEqual(Lookup('field_2'), 'Gouda'))


class Parametrization(ViktorParametrization):
    field_1 = OptionField('Cheese or Peanut butter', options=['Cheese', 'Peanut butter'])
    field_2 = OptionField("What kind of Cheese do you want?", options=['Gouda', 'Beemster'], visible=is_cheese)
    field_3 = NumberField("Do you want 30 + or 48 + Gouda Cheese", visible=is_gouda)

Let me know if any of the above mentioned examples suit your needs. If not, could you indicate why the examples do not work?

(By the way, life would have been much easier if you just had chosen Peanut butter, and not gave the option :wink: )

Hi,

Thanks for the elaborate explanation! This will make the code more readable indeed :slight_smile:
I do still struggle with having ‘too’ many callback functions now, especially when I want to combine different ones.

So, to make life more complex: Is it possible to combine different callback functions within visible? Or combine callback functions with the normal way to use visible?

For example, there is an independent question where I ask if they want a large or small baguette. And I only want field_3 (about the 30 + and 48 + Gouda) to show if they choose a large baguette (and if they choose Cheese and Gouda). I prefer not to combine these in a callback function because then I will have to make >20 different callback functions (not in this case, but in my current app).

def is_large(params, **kwargs): # new
    return params.field_0 == 'Large'

def is_cheese(params, **kwargs):
    return params.field_1 == 'Cheese'

def is_gouda(params, **kwargs):
    if is_cheese(params):
        return params.field_2 == 'Gouda'
    return False

class Parametrization(ViktorParametrization):
    field_0 = OptionField('Large or small baguette?', options=['Large', 'Small']) # new
    field_1 = OptionField('Cheese or Peanut butter', options=['Cheese', 'Peanut butter'])
    field_2 = OptionField("What kind of Cheese do you want?", options=['Gouda', 'Beemster'], visible=is_cheese)
    
    field_3 = NumberField("Do you want 30 + or 48 + Gouda Cheese", visible=[is_gouda, is_large]) # new preferred
    or
    field_3 = NumberField("Do you want 30 + or 48 + Gouda Cheese", visible=[is_gouda, IsEqual(Lookup('field_0'), 'Large')]) # new alternative

So I would like to achieve something like the last line or second-last line, where I combine both callback functions without having to make a new callback function.

I know it’s getting quite messy, so I am trying to make it as not messy as possible :grimacing:

I like your suggestion of stacking callback functions. I will take this feedback and put it on our internal issue board.

Thanks for contributing!

I’ve taken some thought in this limitation, and came to the idea that you can also build something yourself that can help with this. In this example I introduce you a function that can act as an alternative the And object: myAnd

Here it is used in one of the previous code snippets. It unfortunately only works with callback functions, but may help you in concatenating all your callbacks without writing a new callback function.

from viktor import ViktorController
from viktor.parametrization import ViktorParametrization, NumberField, OptionField


def is_cheese(params, **kwargs):
    return params.field_1 == 'Cheese'


def is_gouda(params, **kwargs):
    if is_cheese(params):
        return params.field_2 == 'Gouda'
    return False


def myAnd(*args):
    def my_function(params, **kwargs):
        return all([arg(params, **kwargs) for arg in args])
    return my_function


class Parametrization(ViktorParametrization):
    field_1 = OptionField('Cheese or Peanut butter', options=['Cheese', 'Peanut butter'])
    field_2 = OptionField("What kind of Cheese do you want?", options=['Gouda', 'Beemster'], visible=is_cheese)
    field_3 = NumberField("Do you want 30 + or 48 + Gouda Cheese", visible=myAnd(is_cheese, is_gouda))

I hope this helps.

Ah, that’s a great solution, thanks! :smiley:
This will reduce the number of callback functions needed by a lot, so this solves my problem.

1 Like

I have a question similar to the part of the original question that seems to have gone unanswered: [quote=“Marieke, post:1, topic:1044”]
empty/reset the field when it becomes invisible
[/quote]
If I enter these values in this order:
field_1: Cheese
field_2: Gouda
field_3: 30
field_2: Beemster
Then the value field_3 will still be 30, I would like it to reset to no value.

Hi Thomas,

Thank you for contributing to the thread, and highlighting this part. Unfortunately this is one of the limitations of the VIKTOR platform, where one can only change parameters through entering/removing the value from the field manually, or using the SetParamsButton. I.e. setting parameters on the trigger event of a parameter that changes is currently not supported.

Although I see some discussions that hint towards what you would like to have, there are no feature requests that I can find that clearly present this. It would be good to write your case out as a feature request.

That being said, I believe that this feature request sort of already explains the case. An upvote will then be adequate, with maybe a comment of your case.

Thanks for your reply Marcel. How do I upvote or change this thread to a feature request?

Hi @thomasvdl ,

This thread is already a feature request, as it has been posted on the Feature Request board. To upvote, simply click on the button next to the heading: