Repository: wagtail/wagtail

Issue:
FormBuilder preview: no clean_name
### Issue Summary

When creating a AbstractForm page, there are no `clean_name` for any `form_fields` attributes when previewing the page.  So only one is shown. When the page is published, fields related to this AbstractForm page are created, an they appear normally.

### Steps to Reproduce

```
git clone https://github.com/Sekun/wagtail-issue-formbuilder
cd wagtail-issue-formbuilder
python3 -m venv .env
source .env/bin/activate
pip install -r requirements.txt
./manage.py migrate
./manage.py loaddata dump.json.gz
```

- Go to page /admin/pages/3/edit/
- Click on preview
- Only one field is shown.

There are no `clean_name` in wagtail revision: `"form_fields":[{"pk": null, "sort_order": 0, "clean_name": "", "label": "test", "field_type": "number", "required": true, "choices": "", "default_value": "", "help_text": "", "page": 3}...`


-    I have confirmed that this issue can be reproduced as described on a fresh Wagtail project: yes

### Fix ?

Update `get_form_fields`: set `clean_name` if not defined.

```py
class BuilderPage(AbstractForm):
    content_panels = Page.content_panels + [
        InlinePanel("form_fields", label="Form fields"),
    ]


class CustomField(AbstractFormField):
    page = ParentalKey(
        BuilderPage, on_delete=models.CASCADE, related_name="form_fields"
    )

    def get_form_fields(self):
        fields = super().get_form_fields().all()
        for field in fields:
            if not field.clean_name:
                field.clean_name = get_field_clean_name(field.label)
        return fields
```

### Technical details

* Python version: Python 3.8.6
* Django version: Django==2.2.17
* Wagtail version: wagtail==2.11.1





Discussion:
Same issue is present in wagtail 2.13

Proposed workaround works, but `get_form_fields` should be added to `AbstractForm` subclass:

```python
class BuilderPage(AbstractForm):

    def get_form_fields(self):
        fields = super().get_form_fields().all()
        for field in fields:
            if not field.clean_name:
                field.clean_name = get_field_clean_name(field.label)
        return fields
```

Resolving patch:
diff --git a/wagtail/contrib/forms/forms.py b/wagtail/contrib/forms/forms.py
--- a/wagtail/contrib/forms/forms.py
+++ b/wagtail/contrib/forms/forms.py
@@ -108,7 +108,12 @@ def formfields(self):
         for field in self.fields:
             options = self.get_field_options(field)
             create_field = self.get_create_field_function(field.field_type)
-            formfields[field.clean_name] = create_field(field, options)
+
+            # If the field hasn't been saved to the database yet (e.g. we are previewing
+            # a FormPage with unsaved changes) it won't have a clean_name as this is
+            # set in FormField.save.
+            clean_name = field.clean_name or get_field_clean_name(field.label)
+            formfields[clean_name] = create_field(field, options)
 
         return formfields
 


Test patch:
diff --git a/wagtail/contrib/forms/tests/test_forms.py b/wagtail/contrib/forms/tests/test_forms.py
--- a/wagtail/contrib/forms/tests/test_forms.py
+++ b/wagtail/contrib/forms/tests/test_forms.py
@@ -3,6 +3,7 @@
 from django.test import TestCase
 
 from wagtail.contrib.forms.forms import FormBuilder
+from wagtail.contrib.forms.utils import get_field_clean_name
 from wagtail.core.models import Page
 from wagtail.tests.testapp.models import (
     ExtendedFormField,
@@ -195,6 +196,32 @@ def test_fields(self):
             form_class.base_fields["a_hidden_field"].widget, forms.HiddenInput
         )
 
+    def test_unsaved_fields_in_form_builder_formfields(self):
+        """Ensure unsaved FormField instances are added to FormBuilder.formfields dict
+        with a clean_name as the key.
+        """
+        unsaved_field_1 = FormField(
+            page=self.form_page,
+            sort_order=14,
+            label="Unsaved field 1",
+            field_type="singleline",
+            required=True,
+        )
+        self.form_page.form_fields.add(unsaved_field_1)
+
+        unsaved_field_2 = FormField(
+            page=self.form_page,
+            sort_order=15,
+            label="Unsaved field 2",
+            field_type="singleline",
+            required=True,
+        )
+        self.form_page.form_fields.add(unsaved_field_2)
+
+        fb = FormBuilder(self.form_page.get_form_fields())
+        self.assertIn(get_field_clean_name(unsaved_field_1.label), fb.formfields)
+        self.assertIn(get_field_clean_name(unsaved_field_2.label), fb.formfields)
+
 
 class TestCustomFormBuilder(TestCase):
     def setUp(self):