In this tutorial we will be using VueJs to create a Markdown editor which uses GitHub's stylesheets and markdown rendering API to replicate the GitHub Markdown style. This can be a handy little utility to preview what your READMEs, comments, or issues would look like when rendered on GitHub.

More importantly, this tutorial aims to illustrate how you can use vue-resouce in your Vue projects to make web requests and handle responses via AJAX. Check out the demo to see what we'll be creating in this tutorial and if you haven't read VueJs: The Basics I'd encourage you to skim through it.

Setting Up Our Page

Let's start off by creating the basic structure for our HTML page. Go ahead and create a directory with an empty HTML file. You can call this HTML file whatever you like, I'll just use index.html:

<!DOCTYPE html>
<html>

<head>
    <title>GitHub Markdown Preview with VueJs - coligo</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
    <link rel="stylesheet" href="github-markdown.css">

    <style>
        body {
            background-color: #f4f7f7;
        }

        .header-text h4 {
            color: #454552;
        }

        .header-text h1 {
            color: #e85a71;
        }

        #editor,
        #preview {
            height: 500px;
            max-height: 500px;
            margin-bottom: 20px;
            margin-top: 20px;
        }

        .panel {
            overflow-y: auto;
            max-height: 500px;
            padding: 10px;
        }

        .container {
            padding-bottom: 30px;
        }
    </style>
</head>

<body>
    <div id="app" class="container">
        <div class="row">
            <div class="col-xs-12 header-text">
                <h1>GitHub Markdown Preview</h1>
                <h4>coligo.io</h4>
            </div>
        </div>
        <div class="row">
            <div class="col-xs-12 col-md-6">
                <textarea id="editor" class="form-control"></textarea>
            </div>
            <div id="preview" class="col-xs-12 col-md-6">
                <div class="panel panel-default">
                    <div class="panel-body">
                        <article class="markdown-body">

                        </article>
                    </div>
                </div>
            </div>
        </div>
        <div class="row">
            <div class="col-xs-12">
                <button type="button" class="btn btn-primary pull-right">Preview</button>
            </div>
        </div>
    </div>

    <script src="https://code.jquery.com/jquery-2.2.0.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
</body>

</html>

If you save this HTML file and open it in your browser, you should see a simple page with a heading at the top, a text area for Markdown on the left, a panel to display the rendered Markdown on the right, and a preview button at the bottom. Just like you saw in the demo!

The HTML for this page is rather simple - we are using Twitter Bootstrap for responsiveness and some basic inline styles to format the page the way we want to.

Let's go ahead and download the github-markdown.css stylesheet that we included at the top of our HTML file. This stylesheet contains the CSS required to replicate the GitHub Markdown style. You can find this stylesheet in the sindresorhus/github-markdown-css repository. Be sure to save this file in the same folder you created your HTML file in.

We now have the basic structure for our Markdown editor and the GitHub Markdown stylesheet in place. Let's move on to the next step where we will add VueJs and vue-router to this page so we can start making HTTP calls to the GitHub API.

Calling GitHub's API with vue-resource

In this step we will be using VueJs to:

  1. grab the Markdown that the user has entered in the textarea using the v-model directive
  2. use vue-resource to pass the user's Markdown to the GitHub API via a POST request to render the Markdown to HTML
  3. render the resulting HTML to the preview panel on the right using triple curly braces {{{ }}}

Before we jump into implementing the steps noted above, let's include VueJs and vue-resource in our HTML page via a CDN and create a new Vue instance at the bottom of our index.html file:

<!-- index.html -->
...
<script src="https://cdn.jsdelivr.net/vue/1.0.16/vue.js"></script>
<script src="https://cdn.jsdelivr.net/vue.resource/0.7.0/vue-resource.min.js"></script>

<script>
    var vm = new Vue({
        el: "#app"
    });
</script>
...

All we're doing here is binding our Vue instance to the root div with an id of app. Now that we have our Vue instance in place, let's start implementing steps 1-3. Step 1:

grab the Markdown that the user has entered in the textarea using the v-model directive

To do this we will need to create a data object in our Vue instance with a property to store the raw markdown that the user enters in the textarea:

<!-- index.html -->
...
    <div class="col-xs-12 col-md-6">
        <textarea id="editor" class="form-control" v-model="rawMarkdown"></textarea>
    </div>
...
    <script>
        var vm = new Vue({
            el: "#app",
            data: {
                rawMarkdown: ''
            }
        });
    </script>
...

Now any Markdown that the user types in the textarea on the left will be stored in the rawMarkdown property, thanks to the two-way binding achieved through the v-model directive. We will be passing the contents of the rawMarkdown property to the GitHub Markdown API to render it into HTML, which we can then display in our preview panel on the right.

This brings us to step 2:

use vue-resource to pass the user's Markdown to the GitHub API via a POST request to render the Markdown to HTML

When the user clicks the preview button, we want to call the GitHub API, so let's add a click handler to the button that will trigger a method called renderPreview:

<button type="button" class="btn btn-primary pull-right" @click="renderPreview">Preview</button>

Here's where vue-resource comes in! The renderPreview method will take whatever is in the rawMarkdown property and pass it to the /markdown endpoint via a POST request. Once GitHub responds with the result, we will store it in a property called renderedMarkdown:

var vm = new Vue({
    el: "#app",
    data: {
        rawMarkdown: '',
        renderedMarkdown: ''
    },
    methods: {
        renderPreview: function() {
            // POST request to GitHub API
            this.$http({
                url: 'https://api.github.com/markdown',
                method: 'POST',
                data: {text: this.rawMarkdown, mode: 'gfm'}
            }).then(function (response) {
                // success callback
                this.renderedMarkdown = response.data;
            }, function (response) {
                // error callback
                console.log(response.data);
            });
        }
    }
});

Let's take a second and break down what's happening in the renderPreview method. vue-resource allows us to make HTTP calls in one of 2 ways:

  1. Globally using Vue.http(options)
  2. or in a Vue instance, like we're doing above: this.$http(options)

They both take an options object in which you can specify a number of parameters. You can have a look at all these parameters in the documentation if you're interested. However for our purposes we only need to specify 3 parameters:

  • url - the endpoint for GitHub's Markdown rendering API
  • method - the /markdown endpoint expects POST as the HTTP method
  • data - the data we want to send to GitHub, which can be an object or a string

In our case we want to send the raw markdown text that the user has entered, which is accessible via this.rawMarkdown (remember: 'this' keyword points to the Vue instance). The second parameter we are sending in our data object is the mode, which simply tells GitHub to render the raw markdown we send it as GitHub Flavored Markdown (gfm) which is just GitHub's own version of the Markdown syntax.

The http(options) method returns a promise which we can resolve in the following manner:

this.$http(options).then(successCallback, errorCallback)

Both the successCallback and the errorCallback receive a response object as a parameter which contains useful properties such as the data returned from the server, the status code and text, headers, etc...

In our success callback, we want to render the data returned from the API endpoint to the preview panel on the right. To do so we are assigning our local data property, this.renderedMarkdown, to response.data which contains the rendered Markdown that the GitHub API returned to us.

Finally, we are at step 3:

render the resulting HTML to the preview panel on the right using triple curly braces {{{ }}}

In our <article> tag within our preview panel, we will use the triple curly braces (not double curly braces) to update the element's innerHTML with plain HTML. You can read about the v-html directive or it's equivalent triple curly braces in the VueJs API.

<article class="markdown-body">
    <!-- note the triple curly braces -->
    {{{renderedMarkdown}}}
</article>

Now whenever the user changes their Markdown and hits the preview button, the preview panel to the right will automatically be updated with the new response from the API.

We're good to go! Here's what the entire HTML file should look like, more or less:

<!DOCTYPE html>
<html>

<head>
    <title>GitHub Markdown Preview with VueJs - coligo</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
    <link rel="stylesheet" href="github-markdown.css">

    <style>
        body {
            background-color: #f4f7f7;
        }

        .header-text h4 {
            color: #454552;
        }

        .header-text h1 {
            color: #e85a71;
        }

        #editor,
        #preview {
            height: 500px;
            max-height: 500px;
            margin-bottom: 20px;
            margin-top: 20px;
        }

        .panel {
            overflow-y: auto;
            max-height: 500px;
            padding: 10px;
        }

        .container {
            padding-bottom: 30px;
        }
    </style>
</head>

<body>
    <div id="app" class="container">
        <div class="row">
            <div class="col-xs-12 header-text">
                <h1>GitHub Markdown Preview</h1>
                <h4>coligo.io</h4>
            </div>
        </div>
        <div class="row">
            <div class="col-xs-12 col-md-6">
                <textarea id="editor" class="form-control" v-model="rawMarkdown"></textarea>
            </div>
            <div id="preview" class="col-xs-12 col-md-6">
                <div class="panel panel-default">
                    <div class="panel-body">
                        <article class="markdown-body">
                            <!-- note the triple curly braces -->
                            {{{renderedMarkdown}}}
                        </article>
                    </div>
                </div>
            </div>
        </div>
        <div class="row">
            <div class="col-xs-12">
                <button type="button" class="btn btn-primary pull-right" @click="renderPreview">Preview</button>
            </div>
        </div>
    </div>

    <script src="https://cdn.jsdelivr.net/vue/1.0.16/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/vue.resource/0.7.0/vue-resource.min.js"></script>
    <script src="https://code.jquery.com/jquery-2.2.0.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>

    <script>
        var vm = new Vue({
            el: "#app",
            data: {
                rawMarkdown: '',
                renderedMarkdown: ''
            },
            methods: {
                renderPreview: function() {
                    // POST request to GitHub API
                    this.$http({
                        url: 'https://api.github.com/markdown',
                        method: 'POST',
                        data: {text: this.rawMarkdown, mode: 'gfm'}
                    }).then(function (response) {
                        // success callback
                        this.renderedMarkdown = response.data;
                    }, function (response) {
                        // error callback
                        console.log(response.data);
                    });
                }
            }
        });
    </script>
</body>

</html>

Wrapping Up

We went over how we can include vue-resource in our Vue projects and used it to make HTTP calls to the GitHub API. As you have seen for yourself, vue-resource has a simple and easy to use syntax for making HTTP requests, similar to that of jQuery.

Be sure to have a look at the shorthand notation for using the http function and see which method you prefer.

Feel free to go through the rest of the tutorials on coligo or suggest a tutorial in the comments below!