Visibility conditionality and default parameters in Dynamic Arrays

Hi,
I am trying to use Dynamic Arrays inside a tab, for users to add different “rows” and set parameters inside each row. The user should be able to set parameters to default values either linked to variables (not hardcoded) or using the SetParamsButton. Also, these parameters should be visible or not depending on values of other parameters on the same row.
For example, doing something like the script below, where I have Dynamic Arrays on a Tab and the “Value” number field needs to appear only when the “Type” option field is set to “Type 1”.

`parameters.tab = Tab("Tab 1")
parameters.tab.array = DynamicArray("Array")
parameters.tab.array.type= OptionField(
    "Type",
    options=["Type 1", "Type 2"])
parameters.tab.array.value = NumberField(
        "Value", visible=IsEqual(RowLookup("type"),"Type 1"))`

Is this possible? If not, is it planned to be added soon as a new feature?
Right now we found a work-around to this but requires lots of logics to be accomplished.

Thanks
Andres

Hi Andres,

Thank you for the clear example. Is this what you are looking for?

from viktor import ViktorController
from viktor.parametrization import ViktorParametrization, NumberField, OptionField, DynamicArrayConstraint, IsTrue, \
    Lookup, DynamicArray, IsEqual, Tab

_show_value = DynamicArrayConstraint('tab.array', IsEqual(Lookup('$row.type'), 'Type 1'))


class Parametrization(ViktorParametrization):
    tab = Tab("Tab 1")
    tab.array = DynamicArray("Array")
    tab.array.type = OptionField("Type", options=["Type 1", "Type 2"])
    tab.array.value = NumberField("Value", visible=_show_value)

This approach worked for conditional visibility, thanks! I will explore it further.
With respect to my other question, is there anything that can be done with respect to the use of SetParamsButton / action buttons inside Dynamic Arrays, to set default parameters values for field without them being hard-corded? Right now I am getting “Action buttons are not allowed in a dynamic array.”

It is correct that you can only have buttons outside a DynamicArray. That being said, you could still perform setting functionalities with DynamicArrays, but then on a button outside the DynamicArray. In what way would you be limited if you cannot have a button in a DynamicArray?

Inside the Dynamic Array, I want to set default parameter values to different fields. These default parameter values shouldn’t be hard-coded, instead, they should be linked to a dictionary with default settings (which I import as a module). How can I link default parameters values to a variable?

The SetParamsButton would just be useful to trigger the parameter field to be filled, but not a must if I can link default parameter values to a variable.

You can write any type of logic that can result in values to be filled in fields, whether these values are hardcoded, calculated, or retrieved from other sources.

Would your default values for a given row of the DynamicArray be determined by what is selected within the DynamicArray? It will help if you can give a concrete example.

Take as example the following:

from viktor import ViktorController
from viktor.parametrization import ViktorParametrization, NumberField, OptionField, DynamicArrayConstraint, IsTrue, Lookup, DynamicArray, IsEqual, Tab, SetParamsButton
from viktor.result import SetParamsResult
from app.submodules.module import default_specs

_show_value = DynamicArrayConstraint('tab.array', IsEqual(Lookup('$row.type'), 'Type 1'))


class Parametrization(ViktorParametrization):
    tab = Tab("Tab 1")
    tab.array = DynamicArray("Array")
    tab.array.type = OptionField("Type", options=["Type 1", "Type 2"])
    tab.array.value = NumberField("Value", visible=_show_value)
    tab.array.fill_defaults= SetParamsButton(
        "Set default", method="fill_defaults_params")
    
class Controller(ViktorController):
    label = '...'
    parametrization = Parametrization
    
    def fill_defaults_params(self, params: Munch, **kwargs):
        params.tab.array.value = default_specs.specs[default_specs.type_1]["value"]
        return SetParamsResult(params)

where in default_specs.py, which I am importing, I have a dictionary with default specs:

specs = {
    type_1: {
        "value": 5,
    }
}

I know this approach doesn’t work because SetParamsButton is not allowed inside Dynamic Arrays.
Could I do something like this instead?

class Parametrization(ViktorParametrization):
    tab = Tab("Tab 1")
    tab.array = DynamicArray("Array")
    tab.array.type = OptionField("Type", options=["Type 1", "Type 2"])
    tab.array.value = NumberField("Value", visible=_show_value, default=default_specs.specs[default_specs.type_1]["value"])

Ah okey, now I see what you mean.

The short answer is: Yes, it is possible what you are trying to achieve. Although the SetParamsButton cannot be within the array, the logic can alter the values within the array. Here is a snippet that probably solves your issue. Let me know whether it does.

from munch import Munch
from viktor import ViktorController, UserError
from viktor.parametrization import ViktorParametrization, NumberField, OptionField, DynamicArrayConstraint, \
    Lookup, DynamicArray, IsEqual, Tab, SetParamsButton, OptionListElement
from viktor.result import SetParamsResult


default_specs = {
    'type_1': {"value": 5},
    'type_2': {"value": 10},
}

_show_value = DynamicArrayConstraint('tab.array', IsEqual(Lookup('$row.type'), 'type_1'))


class Parametrization(ViktorParametrization):
    tab = Tab("Tab 1")
    tab.array = DynamicArray("Array")
    tab.array.type = OptionField("Type", options=[OptionListElement(value='type_1', label="Type 1"),
                                                  OptionListElement(value='type_2', label="Type 2")])
    tab.array.value = NumberField("Value", visible=_show_value)

    tab.setparams_btn = SetParamsButton('Set default values', 'fill_defaults_params')


class Controller(ViktorController):
    label = '...'
    parametrization = Parametrization

    def fill_defaults_params(self, params: Munch, **kwargs):
        for row in params.tab.array:
            if row.type not in default_specs:
                raise UserError(f'Type {row.type} not selected or defined.')
            row.value = default_specs[row.type]["value"]
        return SetParamsResult(params)

I think this will help me achieve what I need. Thanks!

1 Like