matthewn
matthewn

Reputation: 309

Embedding a Form Builder page in another Wagtail page

The Wagtail Form Builder documentation states:

form_page.html differs from a standard Wagtail template in that it is passed a variable form, containing a Django Form object, in addition to the usual page variable.

But in my current project, I need to embed a contact form (implemented as a Form Builder page) in another page. In the model for that target page, I'm using a PageChooserPanel to let an editor select the form page to embed. In other words:

class TargetPage(Page):
    [...snip...]  
    contact_form = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
    )

    content_panels = Page.content_panels + [
        [...snip...]
        PageChooserPanel('contact_form', 'FormPage'),
    ]

My problem is in the template for the target page. I can access the attributes of the form page via page.contact_form, but since this page doesn't have a form object passed in, I can't figure out how to render my fields.

I am guessing that I need to override my target page's get_context() so that it includes the form object I need. But I can't figure out how to get that object. Can some kind soul put me on the right track?

Upvotes: 4

Views: 666

Answers (2)

Sopan
Sopan

Reputation: 691

#home/models.py

class HomePage(Page):
    subscription_form = models.ForeignKey(
        'wagtailcore.Page',
        null=True,          
        blank=True,                
        on_delete=models.SET_NULL,
        related_name='+',
    )
    content_panels = Page.content_panels + [
        PageChooserPanel('subscription_form', 'home.FormPage'),
    ]
    def attached_form(self):
       return self.subscription_form.specific.get_form()
    def attached_form_url(self):
        return self.subscription_form.get_url();
    

.... #home/models.py

class FormField(AbstractFormField):
    page = ParentalKey(
        "FormPage", related_name="form_fields", on_delete=models.CASCADE)

... #home/models.py

class FormPage(AbstractEmailForm):
    image = models.ForeignKey(
        "wagtailimages.Image",
        null=True, 
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
    )   
    body = StreamField(BaseStreamBlock(), use_json_field=True)
    thank_you_text = RichTextField(blank=True)
    
    # Note how we include the FormField object via an InlinePanel using the
    # related_name value
    content_panels = AbstractEmailForm.content_panels + [
        FieldPanel("image"),
        FieldPanel("body"),
        InlinePanel("form_fields", heading="Form fields", label="Field"),
        FieldPanel("thank_you_text"),
        MultiFieldPanel(
            [
                FieldRowPanel(
                    [
                        FieldPanel("from_address"),
                        FieldPanel("to_address"),
                    ]
                ),
                FieldPanel("subject"),
            ],
            "Email",
        ),
    ]

In "home/templates/home/home_page.html

I am using tailwind css.

<style>

input, textarea {
    width: 100%;
    background: rgb(209 213 219 / var(--tw-bg-opacity));
    border-radius: 6px;
    padding: 0.75rem;

}

</style>

<form class="pt-16 form" action="{{ page.attached_form_url }}" method="POST">
        {% csrf_token %}
        {% for field in page.attached_form %}
<div class="pt-10 grid grid-cols-1 md:grid-cols-5 px-4">
<div> </div>

<div class="col-span-3 w-full">
    <div class="container flex flex-col xl:flex-row mx-auto px-5 py-8 xl:py-14 text-gray-500 bg-gray-200 rounded-2xl">
        <div class="w-full mb-6 xl:mb-0 sm:text-center">
            <div class="mb-4 text-gray-900 text-3xl font-extrabold">Join 2,000+ subscribers</div>
            <div class="text-lg">Stay in the loop with everything you need to know.</div>
        </div>
        <div class="w-full">
           <div class="flex flex-col justify-center sm:flex-row gap-3 w-full">
              {{ field }}
              <button type="submit" class="sm:w-1/4 h-12 text-white bg-purple-600 rounded-lg shadow transition-all duration-300 ease-in-out hover:bg-purple-700">Subscribe</button>
          </div>
          <div class="mt-3 text-sm sm:text-center">We care about your data in our <u class="cursor-pointer transition-all duration-300 ease-in-out hover:text-gray-700">privacy policy</u>.</div>
         </div>
     </div>
</div>


<div> </div>
</div>
</form>

enter image description here

I also have

home/templates/home/form_page_landing.html & home/templates/home/form_page.html

Two template files. The "home/templates/home/form_page_landing.html" is utilised when user "subscribes" by email address, the user is redirected to "home/templates/home/form_page_landing.html" page.

Upvotes: 1

matthewn
matthewn

Reputation: 309

After a night's sleep, the answer turned out to be relatively obvious. The missing link is the get_form() method of a Wagtail FormPage. I'm now using this in my TargetPage model:

def attached_form(self):
    return self.contact_form.specific.get_form()

And thus, in my template, I can refer to attached_form to get my fields.

Upvotes: 3

Related Questions