cguardia / questions

Questions is a form library for Python that uses the power of SurveyJS for the UI.
https://questions.readthedocs.io
MIT License
32 stars 9 forks source link

The `visible_if` condition does not work in dynamic panels #362

Closed rilshok closed 1 year ago

rilshok commented 1 year ago

I want to be able to optionally display fields in dynamic panels. But the display predicate visible_if doesn't work in the dynamic panel. I expect that in the example below, the SocialMediaForm.account field should only appear when the service field of the dynamic panel element is set to 'Instagram'. I've tried every possible way to make the condition in the visible_if str, but I still can't achieve the expected behavior. All my attempts have resulted in either the account field always visible or not being displayed in the form. How can I solve this problem?

from flask import Flask, redirect, request

app = Flask(__name__)

from questions import (
    DropdownQuestion,
    Form,
    FormPage,
    FormPanel,
    TextQuestion,
)

class SocialMediaForm(Form):
    service = DropdownQuestion(choices=["Twitter", "Instagram", "Snapchat"])
    account = TextQuestion(visible_if="{service} == 'Instagram'")

class ProfileForm(Form):
    social_media = FormPanel(
        SocialMediaForm,
        title="Social Media Accounts",
        dynamic=True,
        panel_count=1,
    )

class Survey(Form):
    mainpage = FormPage(ProfileForm, title="Main")

@app.route("/", methods=("GET",))
def form():
    form = Survey()
    return form.render_html()

@app.route("/", methods=("POST",))
def post():
    data = request.get_json()
    print(data)
    return redirect("/")

if __name__ == "__main__":
    app.run()
rilshok commented 1 year ago

I found a solution thanks to the Survey Creator on the official SurveyJS website. It turns out that you need to use the panel. prefix to create a ref to the current element of a dynamic panel. In our example, if we change the visible_if condition to "{panel.service} == 'Instagram'", everything will work as we expect.

I also found out that we have the ability to check the values of dynamic panel elements by referring to them by indexes. For example if we use as a condition "{social_media[0].service} == 'Instagram'" then the checking will be done based on the first element of dynamic panel (but in order to be able to refer to social_media array we need to pass dynamic panel name="social_media") to FormPanel init).

cguardia commented 1 year ago

@rilshok Thanks a lot for this note. I didn't see it before you closed the issue, but I added this to the docs.