Ogundipe Samuel software engineer and technical writer

Submitting HTML forms using JavaScript frameworks (Vue, React, Hyperapp)

9 min read 2544

HTML forms can send an HTTP request declaratively while submitting forms and awaiting response. However, you have to wait for a full page reload before getting your results, which most times is not the best user experience.

Forms can also prepare an HTTP request to send via JavaScript, which makes for a better user experience. This article explores ways to do that using three different frameworks: Vue, React, and Hyperapp.

Submitting forms using Vue

Vue is a progressive framework for building user interfaces. Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable. To learn more about Vue, you can visit the official homepage here.

First, let’s define our HTML structure. Create a file named vue.html

<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div class="container" id="app">
    <div class="row">
<div class="col-md-4">
        <div class="panel">
        <h4 class="heading"><strong>Quick </strong> Contact <span></span></h4>
        <div class="form">
            <input type="text" required="" placeholder="Please input your Name" value="" v-model="form.name" class="form-control">
            <input type="text" required="" placeholder="Please input your mobile No" value="" v-model="form.mob" class="form-control">
            <input type="text" required="" placeholder="Please input your Email" value="" v-model="form.email" class="form-control">
            <textarea placeholder="Your Message" v-model="form.mess"  class="form-control"></textarea>
            <input type="submit" value="submit" name="submit" class="btn btn-primary" @click="submitForm()">
        </div>
      </div>
    </div>
</div>
</div>

The code snippet above is a basic HTML declaration in which we:

  • Required the Bootstrap CSS library
  • Required the Vue JavaScript library
  • Required the Axios JavaScript library, this library would make POST requests.
  • Declared 5 elements which comprise 3 input text boxes, one text area, and one button, which would be used to submit the form.

You would notice that in each of the 5 elements, the first 4 declares a v-model attribute to some certain properties of form.

V-model is a way of binding inputs to Vue, such that Vue has the values of these input as they change.

Form does not refer to the HTML form, but refers to an object which we have used for the binding in our Vue component.

Last, if you look at the button element, you would notice a little directive called @click. This directive binds the click event of the button to Vue, instructing Vue on what to do when the button is clicked.

Implementing Vue into the form

In the previous section, we have explained the reason you have seen attributes like v-model in your HTML structure and the @click directive. Here, we show what the Vue part that handles the rest looks like.

Open a script file in your HTML document and paste in:

<script>
var app = new Vue({
    el: '#app',
    data: {
    form: {
	name: '',
	mob: '',
	email: '',
	mess: ''
    }
},
methods: {
  submitForm: function(){
      axios.post('https://httpbin.org/anything', this.form)
      .then(function (response) {
        console.log(response.data);
      })
      .catch(function (error) {
        console.log(error);
      });
  }
}
})
</script>

In the code block above, we defined an Object called form, which comprises our data. Next, we defined a method called submitForm which does an Ajax request to https://httpbin.org/anything. We use httpbin because their service allows us to perform free HTTP methods. The /anything route would return the exact data which we had sent to it.

See how easy it is to submit a form using JavaScript? all you need do is change the URL to that of your server.

Why is my Form is not submitting? Often we note that after writing what looks like the right piece of code, the form does not submit. How do we troubleshoot this? Let me highlight common reasons your Vue form might not submit.

  • The mounted element with the id of app passed into the Vue object with the el key does not exist, and the app is not bound to Vue
  • The click handler on the submit button does not exist/was not attached
  • The axios library was not referenced
  • The Vue library was not referenced

Submitting forms using React

React is a JavaScript library for building user interfaces developed and maintained by Facebook. React makes it painless to create interactive UIs. Design simple views for each state in your application and React will efficiently update and render just the right components when your data changes.

First, let’s define our HTML structure. Create a file named react.html and add:

<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div class="container" id="app">
</div>

The code snippet above is a basic HTML declaration in which we:

  • Required the Bootstrap CSS library
  • Required the React JavaScript library
  • Required the React-Dom JavaScript library
  • Required the Axios JavaScript library, this library would make POST requests
  • Declared a div with the id of app, which would be our root component

Implementing React into the mix

We have a basic setup with the required libraries available and a root element which react would be attached to. Let’s go ahead with the react implementation. Open a script tag and input:

class Root extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          form: {
            name: "",
            mob: "",
            email: "",
            mess: ""
          }
        };
        this._onInputChange = this._onInputChange.bind(this);
        this._onSubmit = this._onSubmit.bind(this);
      }
      _onInputChange(name, e) {
        var form = this.state.form;
        form[name] = e.target.value;
        this.setState(form);
      }
      _onSubmit() {
        axios
          .post("https://httpbin.org/anything", this.state.form)
          .then(function(response) {
            console.log(response.data);
          })
          .catch(function(error) {
            console.log(error);
          });
      }
      render() {
        return (
          <div className="row">
            <div className="col-md-4">
              <div className="panel">
                <h4 className="heading">
                  <strong>Quick </strong> Contact <span />
                </h4>
                <div className="form">
                  <input
                    type="text"
                    required=""
                    placeholder="Please input your Name"
                    className="form-control"
                    onChange={e => this._onInputChange("name", e)}
                  />
                  <input
                    type="text"
                    required=""
                    placeholder="Please input your mobile No"
                    className="form-control"
                    onChange={e => this._onInputChange("mob", e)}
                  />
                  <input
                    type="text"
                    required=""
                    placeholder="Please input your Email"
                    onChange={e => this._onInputChange("email", e)}
                    className="form-control"
                  />
    
                  <textarea
                    placeholder="Your Message"
                    className="form-control"
                    onChange={e => this._onInputChange("mess", e)}
                  />
                  <input
                    type="submit"
                    value="submit"
                    name="submit"
                    className="btn btn-primary"
                    onClick={this._onSubmit}
                  />
                </div>
              </div>
            </div>
          </div>
        );
      }
    }
    ReactDOM.render(<Root />, document.getElementById("app"));

Let’s take a review of what we have above. Here, in our constructor, we declared an initial state that comprises our form object, we then moved ahead to bind two functions which we will set the state as the input changes and submit the form.

In the _onInputChange function, we receive two arguments, which are:

  • name: the name of the element
  • event: the change event that occurred

We use this two parameters to set the state of the exact input that was changed.

In the _onSubmit function, we fire a post request to the https://httpbin.org/anything endpoint, which returns the exact parameters sent. Here, which is what we use as our server.

Let us take a critical look at the render function, where the elements are being rendered.

Here, we defined 5 elements, which comprise 3 inputs, a text area whose change events are bound to the _onInputChange function, and a button element, whose click event is bound to the _onSubmit method.

Finally, we attached the app to an element on our HTML markup.

Why is my Form not displaying? I bet you have been getting a blank screen and cannot understand where the error is coming from.

Taking a quick look at the render function, you would notice we have jsx syntax in there. Now, here is the catch. Unless you are using babel to compile your app, jsx would most likely fail. This is because jsx isn’t regular javascript syntax, and here, we are using the browser build of React.

So how do we solve this? It’s a simple fix.

Any JSX block can be converted into a call to React.createElement with three arguments:

  • The element to create, e.g div, span, ul, e.t.c.
  • A properties object which specifies any property values to be set on that element e.g class, style, required, e.t.c.
  • Any child elements to place in it. This could be a string or other calls to React.createElement to get more elements.

Replace the render function with this:

render() {
        return (
            React.createElement("div", { className: 'row' }, [
                React.createElement("div", { className: 'col-md-4' }, [
                    React.createElement("div", { className: 'panel' }, [
                        React.createElement("h4", {}, 'Quick Contact'),
                        React.createElement("div", { className: 'form' }, [
                            React.createElement("input", {
                                type: 'text',
                                placeholder: "Please input your Name",
                                className: "form-control",
                                name: 'name',
                                onChange: (e) => this._onInputChange('name', e)
                            }),
                            React.createElement("input", {
                                type: 'text',
                                placeholder: "Please input your Mobile number",
                                className: "form-control",
                                name: 'mob',
                                onChange: (e) => this._onInputChange('mob', e)
                            }),
                            React.createElement("input", {
                                type: 'text',
                                placeholder: "Please input your Email",
                                className: "form-control",
                                name: 'email',
                                onChange: (e) => this._onInputChange('email', e)
                            }),
                            React.createElement("textarea", {
                                placeholder: "Please your message",
                                className: "form-control",
                                name: 'mess',
                                onChange: (e) => this._onInputChange('mess', e)
                            }),
                            React.createElement("button", {
                                type: 'button',
                                className: "btn btn-primary",
                                onClick: () => this._onSubmit()
                            }, "submit"),
    
                        ])
                    ])
                ]),
    
            ])
        );
    }

Also, update the ReactDom.render call to this:

ReactDOM.render(
    React.createElement(Root, null),
    document.getElementById('app')
);

Why is my form not submitting? Even after performing each step we think is necessary and cross-checking our code, it is possible your form does not still submit, how do we trouble-shoot this?

  • Ensure that your console is not throwing up errors
  • Ensure that the click and change events are bounded correctly
  • Cross check that the axios library or the library you use for post requests is referenced

Submitting forms using HyperApp

HyperApp is a JavaScript micro-framework for building web applications. This framework has aggressively minimized the concepts you need to understand to be productive while remaining on par with what other frameworks can do.

HyperApp holds firm on the functional programming front when managing your state, but takes a pragmatic approach to allowing for side effects, asynchronous actions, and DOM manipulations.

First, let’s define our HTML structure. Create a file named hyper.html and add:

<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="https://unpkg.com/hyperapp"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div class="container" id="app">
</div>

The code snippet above is a basic HTML declaration in which we:

  • Required the Bootstrap CSS library
  • Required the Hyperapp JavaScript library
  • Required the Axios JavaScript library, this library would make POST requests
  • Declared a div with the id of app, which would be our root component

Introducing Hyperapp to the app We have a basic setup with the required libraries available and a root element which HyperApp would be attached to. Let’s go ahead with the react implementation. Open a script tag and input:

const h = hyperapp.h;
    const app = hyperapp.app;
    const state = {
      form: {
              name: '',
              mob: '',
              email: '',
              mess: '',
            }
    }
    
    const actions = {
      onInputChange: (event) => state => {
        state.form[event.target.name] = event.target.value;
        return state;
      },
      submitForm: () => {
        console.log(state.form)
    axios.post('https://httpbin.org/anything', state.form)
          .then(function (response) {
          console.log(response.data);
        })
          .catch(function (error) {
          console.log(error);
        });
      }
    }
    
    const view = (state, actions) => (
      h("div", {class: 'row'}, [
        h("div", {class: 'col-md-4'}, [
          h("div", {class: 'panel'}, [
            h("h4", {}, 'Quick Contact'),
            h("div", {class: 'form'}, [
              h("input", {type: 'text', placeholder: "Please input your Name", class:"form-control", 
                          name: 'name',
                         oninput: (e)=>actions.onInputChange(e)}),
              h("input", {type: 'text', placeholder: "Please input your Mobile number", class:"form-control", 
                          name: 'mob',
                         oninput: (e)=>actions.onInputChange(e)}),
              h("input", {type: 'text', placeholder: "Please input your Email", class:"form-control", 
                          name: 'email',
                         oninput: (e)=>actions.onInputChange(e)}),
               h("textarea", {placeholder: "Please your message", class:"form-control",
                              name: 'mess',
                         oninput: (e)=>actions.onInputChange( e)}),
              h("button", {type: 'button', class:"btn btn-primary", 
                         onclick: ()=>actions.submitForm()}, "submit"),
              
            ])
          ])
        ]),
      ])
    )
    app(state, actions, view, document.getElementById('app'))

Let’s take a review of what we have above. Here, we declared an initial state that comprises our form object, we then moved ahead to declare two actions which we will set the state as the input changes and submit the form.

In the onInputChange function, we receive one argument, which is:

  • event: the change event that occurred

We use this two parameters to set the state of the exact input that was changed.

In the _onSubmit function, we fire a post request to the https://httpbin.org/anything endpoint, which returns the exact parameters sent. Here, which is what we use as our server.

Here, we must have seen the similarities between React and Hyperapp. For our purposes, I’ll describe Hyperapp as a lightweight alternative to React.

In the render function of the code above, we would notice the exact similarities to React. In fact, the only differences you would notice is the use of class instead of React’s className and onInput in place of onChange.

For the same reason we did not use jsx in the React form, is the same reason we have not used jsx here. If you use the npm package and prefer to use jsx, please feel free.

Conclusion

In this tutorial, we have seen how easy it is to submit forms using 3 different JavaScript frameworks. We have also seen how to solve common issues when our forms are not displaying or not submitting as intended. Do you have any observations about this tutorials or views you want to share? Let us know in the comments.


Plug: LogRocket, a DVR for web apps

LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single page apps.

Try it for free.

Ogundipe Samuel software engineer and technical writer

Leave a Reply