Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wordpress + Vue.js : Contact Forms

I'm working on a WP theme using Vue.js, all data is being fetched through REST API. so far everything works perfectly in terms of displaying the data.

I'm trying to implement a contact form plugin (Contact Form 7 -- but open to suggestions). This plugin works by adding a shortcode to the PHP, but since I'm not using PHP, rather rendering all the front end in the client side -- I'm confused on how to achieve an integration between these two.

Ideas (My initial approaches...)

1. Hide

I could include the shortcode in my index.php file, with it's visiblity hidden and interact with it through javascript once the user gets to the Contact Page. I'm not very certain about this way as the form really doesn't need to load on entry, just on the Contact Page.

2. Client -> Server

Build a form in the front end, gather the user data and send it to a function in the server side (functions.php). This data then is used by the server to perform the desired submission. Does this makes sense / is it even possible?

So...

I'm just looking for some direction. I'm pretty comfortable working with Wordpress and Vue separately, but I still got my question when it comes to interaction between client side and server side, in this context.

Any suggestions that can help me move forward? I prefer to use Contact Form 7 Plugin as many of my sites use that, but I'm also open to other solutions, ideally managed within Wordpress and not a third party service. Any comments or suggestions are appreciated!

Thanks!

like image 739
mmarquez Avatar asked Jul 06 '17 16:07

mmarquez


1 Answers

This one could be a solution, probably not the most elegant.

For reference, loadPageInformation is the method I used to call the REST API, then, the response is stored in pageInfo, like this:

loadPageInformation: function(slug) {

  this.pageInfo = null;

  // retrieving page data using the page slug.
  axios.get(this.appData.rest_url + '/wp/v2/pages?slug=' + slug)
  .then( response => { this.pageInfo = response.data[0]; } )
  .catch( error => { console.log( error ); } );

},

In your template file:

<template>

    <div class="v-page" v-if="this.$root.pageInfo != null">

        <b-row class="">

            <b-col cols="12">

                <h1>{{ this.$root.pageInfo.title.rendered }}</h1>

                <div class="contact-form"></div>

            </b-col>

        </b-row>

     <!-- footer. -->

     <footer-component></footer-component>

   </div>

</template>

<script> 

export default {

    created: function() {

        this.$root.loadPageInformation(this.$route.params.pageSlug);

    },

    updated: function() {

        $( ".cform7 .wpcf7" ).appendTo( ".contact-form" );

    }
}

</script>

You can notice a jQuery line under the updated method that will basically cut the form from PHP and paste it in the template file. That line is the proposed solution. The form will be called with do_shortcode in PHP:

 <div class="cform7 d-none">

    <?php echo do_shortcode('[contact-form-7 id="104" title="Contact form"]'); ?> 

 </div>

EDIT: A different solution

I tested the previous solution and found some issues. So here a new approach.

Short answer: create a new Custom Post Type to print the shortcodes, then load the generated URL into the Vue component.

Very long answer:

For reference: loading the Vue generated JS and CSS files from functions.php should be the right process. It is going to be useful in next steps.

So I created a Custom Post Type called shortcodes, like this:

 function custom_shortcodes_post() {
    $labels = array(
      'name'               => __( 'Shortcodes' ),
      'singular_name'      => __( 'Shortcode' ),
      'add_new'            => __( 'Add new shortcode' ),
      'add_new_item'       => __( 'Add new shortcode' ),
      'edit_item'          => __( 'Edit shortcode' ),
      'new_item'           => __( 'New shortcode' ),
      'all_items'          => __( 'All shortcodes' ),
      'view_item'          => __( 'See shortcode' ),
      'search_items'       => __( 'Search shortcodes' ),
      'not_found'          => __( 'No shortcodes found' ),
      'not_found_in_trash' => __( 'No shortcodes in trash' ),
      'parent_item_colon'  => '',
      'menu_name'          => 'Shortcodes'
    );
    $args = array(
      'labels'              => $labels,
      'description'         => 'Save shortcodes with specific data',
      'public'              => true,
      'show_in_rest'        => true,
      'publicly_queryable'  => true,
      'show_ui'             => true,
      'show_in_menu'        => true,
      'query_var'           => true,
      'rewrite'             => array( 'slug' => 'shortcodes' ),
      'capability_type'     => 'post',
      'has_archive'         => false,
      'hierarchical'        => false,
      'menu_position'       => 6,
      'supports'            => array( 'title', 'editor' )
    );
    register_post_type( 'shortcodes', $args );
  }
  add_action( 'init', 'custom_shortcodes_post' );

Then I created a new shortcode post, and in the content, I wrote the shortcode that Contact Form 7 gives me. I also created single-shortcodes.php, like this:

<!DOCTYPE html>

   <html <?php language_attributes(); ?>>

       <head>

           <?php wp_head(); ?>

       </head>

       <body style="background:none">

            <?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>

                 <?php the_content(); ?>

            <?php endwhile; endif; ?>

            <?php wp_footer(); ?>

       </body>

</html>

Finally, the template file remains the same but with a different JS content:

<template>

  <div class="v-page" v-if="this.$root.pageInfo != null">

    <b-row class="">

        <b-col cols="12">

            <h1>{{ this.$root.pageInfo.title.rendered }}</h1>

            <div class="contact-form"></div>

        </b-col>

    </b-row>

     <!-- footer. -->

    <footer-component></footer-component>

  </div>

</template>

<script> 

  export default {

    created: function() {

        this.$root.loadPageInformation(this.$route.params.pageSlug);

    },

    updated: function() {

        if (this.$route.params.pageSlug == 'contact') {

            $( '.contact-form' ).load( '/shortcodes/contact-form/' );

        }
    }
 }

</script>

After that, an error should appear in the console, because single-shortcodes.php is loading the JS and CSS files from Vue, so in functions.php I made a little fix:

 if ( !is_singular( 'shortcodes' ) ) {

     // here I load the JS and CSS files from Vue.

 }

My entire piece of code to register CSS and JS files looks like this:

 function rci_theme_enqueue() {

    if ( !is_singular( 'shortcodes' ) ) {

        // enqueue main style.
        wp_enqueue_style( 
            'app', 
            get_template_directory_uri() . '/spa/dist/css/app.css' 
        );

        // register the script.
        wp_register_script( 
            'vue_app', 
            get_template_directory_uri() . '/spa/dist/app.js',
            array(), 
            '1.0.0', 
            true 
        );

        // localize the script with new data.
        global $post;
        $app_data = array(
            'rest_url' => untrailingslashit( esc_url_raw( rest_url() ) ),
            'theme_url' => get_template_directory_uri(),
            'app_path' => $post->post_name, // page where the custom page template is loaded.
        );

        wp_localize_script( 'vue_app', 'app_data', $app_data );

        // enqueued script with localized data.
        wp_enqueue_script( 'vue_app' );
    }
}
add_action( 'wp_enqueue_scripts', 'rci_theme_enqueue' );

Again: to me, it does not sound like an elegant solution, but unfortunately, when mixing Vue with WordPress, you will lose some of the WordPress core features.

Hope it helps to someone.

like image 118
ZoiX Avatar answered Oct 11 '22 16:10

ZoiX