Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple submissions in one multi-page form #2045

Open
nestordedios opened this issue Aug 29, 2024 · 9 comments
Open

Multiple submissions in one multi-page form #2045

nestordedios opened this issue Aug 29, 2024 · 9 comments
Labels

Comments

@nestordedios
Copy link

Question

When going through a multi-page form is it possible to create new Submission objects/elements that get saved in the database when then form is submitted?

Additional context

I have a multi page form with about 20 pages. In the 3 first pages three repeater fields collect names of people. The other pages are yes-no questions (radio buttons fields) that are meant to be answered by each each name entered in pages 1-3, so there is a loop through all names to ask different questions.

I'm using Formie Events to pick up the input data and start the loop, then when the last page is reached the Submission is reset resetCurrentSubmission() and the current page of the form set to page 4 setCurrentPage().

When all "names" have answered the questions asked in pages 4-20 the form is submitted and only one Submission is saved in the database and listed on the Submissions section in the CMS.

Would it be possible to save one submission per "name" that answers those questions? How can this be achieve in Formie v2?

@engram-design
Copy link
Member

engram-design commented Aug 29, 2024

This might be tricky to do all as one form, because even if you get to the end of the first cycle and create a new submission, you'd need to retain the pages 1-3 to keep track of that name content so you can loop over things.

If I were to approach this, I think I would create two forms, splitting the pages 1-3 with the other yes-no questions. When completing the first form with names, I would redirect off to the second form with a reference to the submission just created, so it'd still be seamless. See guide.

Then, I would actually render the form X amount of times - 1 for each name - so that a submission is created for each user. The first form would be the only one visible (hidden with CSS or JS). I might even have a hidden field to record the original submission against this "data" submission so that I can create a relationship. Then, after the first form is completed successfully, and a submission created, I use JS to hide that form's container, and show the next form's container and continue on.

@nestordedios
Copy link
Author

Hi @engram-design ,

Thanks a lot for your reply. I've been playing a bit around with your suggestion and I think this makes it easier to handle the loop of questions.

Something I'm bumping into now is that it is not being possible to clear irrelevant data in a rendered form or Submission when making changes to the previously filled in value.

Depending on the answer and input given by the user, a different page will be shown, but when going back the submitted data of non relevant fields are not emptied.

Is there a way to tell Formie to clear non relevant data?

For instance when a user interacts with the form as follows:
Page 1 answers YES -> next page is Page 2
Page 2 answers YES -> next page is Page 3
Page 3 answers YES -> next page is Page 4

If at this point the user would get back to Page 2 and answer NO, Page 3 would be skipped but the answer previsouly given on Page 3 (YES) would still be part of the data form and not be cleared. At this point this data is not relevant anymore

It can get more complicated further in the form flow since some changes in previous questions would make the form flow change and previous filled in data will be taken into account to make some calculations and determine the next page based on that data.

Is there a possibility or a way to clear and track the data filled in pages which are not relevant when user input changes? This is most likely to happen when the user goes back and makes some changes.

@engram-design
Copy link
Member

I imagine you have page conditions setup to show/hide pages based on field conditions?

I'm not sure if you followed my suggested approach, but I wouldn't have thought previously filled in data to event exist if the form is submitted and finalised? Once a form has finished it's submission, the submission is no longer tied to the form, and so the form essentially starts "fresh". You can certainly call form.reset() via JavaScript if that helps, but it's tricky to know your full setup without actually seeing it I'm afraid!

@nestordedios
Copy link
Author

@engram-design I did follow your approach and extended it a little bit to fit my use case since users need to be able to come back and make modifications on submitted forms if necessary, but maybe this needs a different approach. I should have be more clear on this point.

After the first form is submitted the form redirects the user to another URL passing the uid value of the submission. Then in this new location the template renders all the forms and submission for editing like this:

{# Try getting the submission id from the request or the session #}
{% if craft.app.request.get('fsid') %}
    {% set submissionId = craft.app.request.getParam('fsid') %}
{% else %}
    {% set submissionId = craft.app.session.get('fsid') %}
{% endif %}

{# Fetch the submission of the root form (Form 1) #}
{% set submissionForm1 = craft.formie.submissions.uid(submissionId).one() %}

{# Ditch any requests that have supplied an invalid submission #}
{% if not submissionForm1 %}
    {% exit 404 %}
{% endif %}

{# Get the form with the questions #}
{% set questionsForm = craft.formie.forms.handle('questions').one() %}

{% for field in submissionForm1.getFieldLayout().getCustomFields() %}

    ...

    {% set questionsFormSubmission =  craft.formie.submissions.form('questions').relatedFormId(submissionId).personType(personType).personFullName(personFullName).one() %}

    <div class="form-container" id="form-{{ loop.index }}">
        {% do craft.formie.populateFormValues("questions", {
            personType: personType,
            personFullName: personFullName,
            relatedFormId: submissionId,
            formIndex: loop.index
        }) %}
        {# Get formIndex from query string or default to 1 #}
        {% set activeFormIndex = craft.app.request.getParam('formIndex') ?? 1 %}

        {# If we have a question forms Submission tell formie to use it for editing #}
        {% if questionsFormSubmission is not empty %}
            {% if not loop.last %}
                {% set index = loop.index + 1 %}
                {% do questionsFormSubmission.form.setSettings({
                    redirectUrl: '/paginas/questions?formIndex=' ~ index,
                }) %}
            {% else %}
                {% do questionsFormSubmission.form.setSettings({
                    redirectUrl: '/paginas/questions?formFinished=true',
                }) %}
            {% endif %}

            <input type="hidden" name="formIndex" value="{{ activeFormIndex }}">

            {% if questionsFormSubmission.form.isFirstPage() and not loop.first %}
                {% set prevIndex = loop.index - 1 %}
                <a href="{{'/paginas/vragen?formIndex=' ~ prevIndex }}">Previous Person</a>
            {% endif %}

            {# Tell Formie we're editing a submission #}
            {% do questionsFormSubmission.form.setSubmission(questionsFormSubmission) %}
            {{ craft.formie.renderForm(questionsFormSubmission.form) }}
        {% else %}
            {# Render the questions form #}
            {% if not loop.last %}
                {% set index = loop.index + 1 %}
                {% do questionsForm.setSettings({
                    redirectUrl: '/paginas/vragen?formIndex=' ~ index,
                }) %}
            {% else %}
                {% do questionsForm.setSettings({
                    redirectUrl: '/paginas/vragen?formFinished=true',
                }) %}
            {% endif %}

            <input type="hidden" name="formIndex" value="{{ activeFormIndex }}">

            {# When the form is completed submitted redirect to a new URL #}
            {% do questionsForm.setSettings({
                redirectUrl: '/paginas/vragen?formIndex=' ~ index,
            }) %}

            {{ craft.formie.renderForm(questionsForm) }}

            {% if questionsForm.isFirstPage() and not loop.first %}
                {% set prevIndex = loop.index - 1 %}
                <a href="{{'/paginas/vragen?formIndex=' ~ prevIndex }}">Previous Person</a>
            {% endif %}
        {% endif %}
    </div>
{% endfor %}

I do not have page conditions to show/hide pages. On a separate module I use Event::on(SubmissionsController::class, SubmissionsController::EVENT_AFTER_SUBMISSION_REQUEST, function (SubmissionEvent $event) { ... } and depending on the submitted data the script determines which page to show next.

What do you suggest to clear non relevant data when the user has made changes to the form/submission?

@engram-design
Copy link
Member

It does depend a little on what page submission strategy you're using, as an Ajax one will require you to reset the form with JS (formElement.reset()), but a Page Reload strategy allows you to fire Twig. You could call {% do form.resetCurrentSubmission() %} which will reset whatever submission you're doing, or there's a few more helper functions like {% do form.setCurrentPage() %}, etc. These are of course also available in PHP if you want to work them into your event as well.

@nestordedios
Copy link
Author

I'm usinng page reload strategy.

I know about {% do form.resetCurrentSubmission() %} but that will clear all the data entered by the user. Making the form look empty.

In my case users will fill in the form and submit it and come back another time to make some changes to the filled in data.

When that happens I'm looking for a way to clear the data that may not be relevant as explained in this use case #2045 (comment)

Is this something Formie can do out of the box? Is there some documentation or a workaround for it?

@engram-design
Copy link
Member

So I think the different behaviour might be how you're managing show/hide pages on your own. Normally, with page conditions (and field conditions) we always wipe the content for fields if they are hidden, to prevent any stale content. This can happen if you fill in a form, get to the end (without submitting), but go back and change field content so that your previously shown fields are hidden. We wouldn't want that content present in the form.

Here we have code that handles that, as soon as a submission is populated from a request.

But if you're not conditionally hiding pages and fields, that's probably not going to be evaluated, so that logic will need to be up to you. Fortunately, you can look through a pages fields and set each field's value to null, as I assume your logic will let you know which page is hidden or visible.

@nestordedios
Copy link
Author

So I think the different behaviour might be how you're managing show/hide pages on your own. Normally, with page conditions (and field conditions) we always wipe the content for fields if they are hidden, to prevent any stale content. This can happen if you fill in a form, get to the end (without submitting), but go back and change field content so that your previously shown fields are hidden. We wouldn't want that content present in the form.

I've tried letting Formie manage the show/hide pages by setting conditions and indeed it works fine as long as the user doesn't submit the form. But in my case if the user has submitted the form and a Submission was created, when loading that Submission for edition it's important that wiping the content of hidden fields still works.

Is there a different way to achieve that?

@nestordedios
Copy link
Author

@engram-design I've been investigating further the possibilities to use conditions when loading Submissions for edition, but I couldn't find anything. Is this something that can be done or it is just not possible? If it is not possible what do you suggest?

One other thing I've noticed is that when I render the form multiple times like the example below, all the rendered forms seem to be the same instance, which means that any input given in #form-1 will automatically be added to the rest of the forms as well. Is there a way to render a fresh new instance on each loop?

{% set form = craft.formie.forms.handle('myFormHandle').one() %}

{% for i in 0..10 %}
    <div class="form-containerr" id="form-{{ loop.index }}">
        {{ craft.formie.renderForm(form) }}
    </div>
{% endfor %}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants
@engram-design @nestordedios and others